From f5e6b618f0e4b81257af18d43efe6b067a8dbf7b Mon Sep 17 00:00:00 2001 From: Josh Black Date: Thu, 10 Mar 2022 13:46:04 -0600 Subject: [PATCH] feat(grid): add narrow grid support to CSS Grid (#10934) * chore: check-in work * refactor(grid): update grid config and add narrow mode * refactor(grid): update grid, add docs and architecture * feat(styles): update implementation to support flexbox and CSS Grid * feat(carbon-react): add support for grid entrypoint * fix(grid): update flex grid mixin * feat(react): add unstable_ColumnHang * test(styles): update config snapshot * refactor(react): update CSSGrid implementation and stories * test(react): update vrt for grid * docs(grid): update grid docs and comments Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .prettierignore | 1 + docs/migration/v11.md | 155 +++-- packages/carbon-react/.storybook/styles.scss | 1 + packages/carbon-react/__tests__/index-test.js | 1 + .../scss/components/aspect-ratio/_index.scss | 9 + packages/carbon-react/scss/grid/_config.scss | 9 + packages/carbon-react/scss/grid/_flexbox.scss | 9 + .../scss/{_grid.scss => grid/_index.scss} | 0 .../carbon-react/scss/utilities/_convert.scss | 9 + .../carbon-react/scss/utilities/_z-index.scss | 9 + packages/carbon-react/src/index.js | 1 + packages/carbon-react/tasks/build-styles.js | 18 +- packages/grid/ARCHITECTURE.md | 53 ++ .../__tests__/__snapshots__/scss-test.js.snap | 2 +- packages/grid/__tests__/scss-test.js | 15 +- packages/grid/docs/sass.md | 74 ++ .../css-grid/src/components/Header.js | 13 - .../grid/examples/css-grid/src/pages/index.js | 504 +++++++++++--- .../examples/css-grid/src/scss/_reset.scss | 2 +- .../css-grid/src/scss/components/_header.scss | 26 - .../grid/examples/css-grid/src/styles.scss | 88 ++- packages/grid/index.scss | 5 +- packages/grid/scss/modules/_breakpoint.scss | 74 +- packages/grid/scss/modules/_config.scss | 78 ++- packages/grid/scss/modules/_css-grid.scss | 631 +++++++++++------- packages/grid/scss/modules/_flex-grid.scss | 67 +- .../__snapshots__/PublicAPI-test.js.snap | 23 + packages/react/src/__tests__/index-test.js | 1 + packages/react/src/components/Grid/CSSGrid.js | 92 ++- packages/react/src/components/Grid/Column.js | 155 ++++- .../react/src/components/Grid/ColumnHang.js | 49 ++ .../Grid/__tests__/Grid-test.e2e.js | 1 - .../Grid/__tests__/Grid-test.e2e.scss | 5 + packages/react/src/components/Grid/index.js | 1 + .../src/components/Grid/next/Grid.stories.js | 124 ++-- .../components/Grid/next/Grid.stories.scss | 123 +++- packages/react/src/index.js | 1 + packages/styles/docs/sass.md | 47 +- .../__snapshots__/config-test.js.snap | 1 + packages/styles/scss/__tests__/grid-test.js | 4 +- packages/styles/scss/_config.scss | 5 + .../scss/{_grid.scss => grid/_config.scss} | 10 +- .../scss/grid/_flexbox.scss} | 5 +- packages/styles/scss/grid/_index.scss | 16 + packages/type/scss/modules/_styles.scss | 2 +- 45 files changed, 1762 insertions(+), 757 deletions(-) create mode 100644 packages/carbon-react/scss/components/aspect-ratio/_index.scss create mode 100644 packages/carbon-react/scss/grid/_config.scss create mode 100644 packages/carbon-react/scss/grid/_flexbox.scss rename packages/carbon-react/scss/{_grid.scss => grid/_index.scss} (100%) create mode 100644 packages/carbon-react/scss/utilities/_convert.scss create mode 100644 packages/carbon-react/scss/utilities/_z-index.scss create mode 100644 packages/grid/ARCHITECTURE.md create mode 100644 packages/grid/docs/sass.md delete mode 100644 packages/grid/examples/css-grid/src/components/Header.js delete mode 100644 packages/grid/examples/css-grid/src/scss/components/_header.scss create mode 100644 packages/react/src/components/Grid/ColumnHang.js rename packages/styles/scss/{_grid.scss => grid/_config.scss} (70%) rename packages/{grid/examples/css-grid/src/scss/components/_index.scss => styles/scss/grid/_flexbox.scss} (72%) create mode 100644 packages/styles/scss/grid/_index.scss diff --git a/.prettierignore b/.prettierignore index ecc5f3ab411f..29094d8fdc95 100644 --- a/.prettierignore +++ b/.prettierignore @@ -46,6 +46,7 @@ packages/icons-react/next/** # Examples packages/**/examples/** !packages/themes/examples/** +!packages/grid/examples/css-grid/** # Yarn **/.yarn/** diff --git a/docs/migration/v11.md b/docs/migration/v11.md index 147cc139d5de..3fe4a9602a24 100644 --- a/docs/migration/v11.md +++ b/docs/migration/v11.md @@ -1316,18 +1316,27 @@ For full Sass API documentation, visit the - This package now requires Dart Sass and uses Sass Modules - The default number of columns has been changed to 16 -- The grid package has been updated to include a CSS Grid-based implementation +- The grid package has been updated to include a CSS Grid implementation of the + grid + - This include supports for subgrid in CSS Grid - Variables, mixins, and functions with the `carbon--` prefix have been renamed -| Filename | v10 | v11 | -| --------------------- | --- | ------------------------------------------------------------------------- | -| `scss/grid.scss` | | Moved to `index.scss` | -| `scss/index.scss` | | Moved to `index.scss` | -| `scss/_css-grid.scss` | | New | -| `scss/flex-grid.scss` | | New | -| `scss/_mixins.scss` | | Moved to `scss/flex-grid.scss` | -| `scss/_prefix.scss` | | Moved to `scss/_config.scss` | -| `scss/12.scss` | | This file has been removed, to configure the grid use `scss/_config.scss` | +| Filename | v10 | v11 | +| ---------------------- | ------------------------- | ------------------------------------------------------------------------------------------- | +| `scss/grid.scss` | | Moved to `index.scss` | +| `scss/index.scss` | | Moved to `index.scss` | +| `scss/_breakpoint` | | New, this has moved from `@carbon/layout` | +| | `$grid-gutter--condensed` | `$grid-gutter` | +| `scss/_css-grid.scss` | | New | +| `scss/_flex-grid.scss` | | New | +| `scss/_mixins.scss` | | Moved to `scss/_flex-grid.scss` | +| | `$carbon--aspect-ratios` | TODO | +| | `carbon--grid` | Renamed to `@mixin flex-grid` | +| `scss/_prefix.scss` | | Moved to `scss/_config.scss` under `$prefix` | +| `scss/12.scss` | | This file has been removed, configure `$flex-grid-columns` to 12 to emulate this entrypoint | + +For full documentation, visit the +[Sass Documentation](../../packages/grid/docs/sass.md) for the package. ## @carbon/layout @@ -1352,69 +1361,69 @@ For full Sass API documentation, visit the **Changes** -| Filename | v10 | v11 | -| :---------------------- | :----------------------------------- | :------------------------------------------------------------------------------------------------------------------------ | -| `scss/_breakpoint.scss` | | Removed, use `@carbon/styles/scss/breakpoint`, `@carbon/react/scss/breakpoint`, or `@carbon/grid/scss/breakpoint` instead | -| `scss/_convert.scss` | `$carbon--base-font-size` | Renamed to `$base-font-size` | -| | `@function carbon--rem` | Renamed to `@function rem` | -| | `@function carbon--em` | Renamed to `@function em` | -| `scss/_key-height.scss` | `@function carbon--get-column-width` | Removed | -| | `$carbon--key-height-scales` | Removed | -| | `@function carbon--key-height` | Removed | -| `scss/_mini-unit.scss` | `$carbon--mini-unit-size` | Removed | -| | `@function carbon--mini-units` | Removed | -| `scss/_spacing.scss` | `$carbon--spacing-01` | Removed, use `$spacing-01` instead | -| | `$carbon--spacing-02` | Removed, use `$spacing-02` instead | -| | `$carbon--spacing-03` | Removed, use `$spacing-03` instead | -| | `$carbon--spacing-04` | Removed, use `$spacing-04` instead | -| | `$carbon--spacing-05` | Removed, use `$spacing-05` instead | -| | `$carbon--spacing-06` | Removed, use `$spacing-06` instead | -| | `$carbon--spacing-07` | Removed, use `$spacing-07` instead | -| | `$carbon--spacing-08` | Removed, use `$spacing-08` instead | -| | `$carbon--spacing-09` | Removed, use `$spacing-09` instead | -| | `$carbon--spacing-10` | Removed, use `$spacing-10` instead | -| | `$carbon--spacing-11` | Removed, use `$spacing-11` instead | -| | `$carbon--spacing-12` | Removed, use `$spacing-12` instead | -| | `$carbon--spacing-13` | Removed, use `$spacing-13` instead | -| | `$carbon--spacing` | Removed, use `$spacing` instead | -| | `$spacing-01` | No changes | -| | `$spacing-02` | No changes | -| | `$spacing-03` | No changes | -| | `$spacing-04` | No changes | -| | `$spacing-05` | No changes | -| | `$spacing-06` | No changes | -| | `$spacing-07` | No changes | -| | `$spacing-08` | No changes | -| | `$spacing-09` | No changes | -| | `$spacing-10` | No changes | -| | `$spacing-11` | No changes | -| | `$spacing-12` | No changes | -| | `$spacing-13` | No changes | -| | `$carbon--layout-01` | Removed, use `$spacing-05` instead | -| | `$carbon--layout-02` | Removed, use `$spacing-06` instead | -| | `$carbon--layout-03` | Removed, use `$spacing-07` instead | -| | `$carbon--layout-04` | Removed, use `$spacing-09` instead | -| | `$carbon--layout-05` | Removed, use `$spacing-10` instead | -| | `$carbon--layout-06` | Removed, use `$spacing-12` instead | -| | `$carbon--layout-07` | Removed, use `$spacing-13` instead | -| | `$carbon--layout` | Removed | -| | `$layout-01` | Removed, use `$spacing-05` instead | -| | `$layout-02` | Removed, use `$spacing-06` instead | -| | `$layout-03` | Removed, use `$spacing-07` instead | -| | `$layout-04` | Removed, use `$spacing-09` instead | -| | `$layout-05` | Removed, use `$spacing-10` instead | -| | `$layout-06` | Removed, use `$spacing-12` instead | -| | `$layout-07` | Removed, use `$spacing-13` instead | -| | `$carbon--fluid-spacing-01` | Removed, use `$fluid-spacing-01` instead | -| | `$carbon--fluid-spacing-02` | Removed, use `$fluid-spacing-02` instead | -| | `$carbon--fluid-spacing-03` | Removed, use `$fluid-spacing-03` instead | -| | `$carbon--fluid-spacing-04` | Removed, use `$fluid-spacing-04` instead | -| | `$carbon--fluid-spacing` | Removed, use `$fluid-spacing` instead | -| | `$fluid-spacing-01` | No changes | -| | `$fluid-spacing-02` | No changes | -| | `$fluid-spacing-03` | No changes | -| | `$fluid-spacing-04` | No changes | -| | `$fluid-spacing` | No changes | +| Filename | v10 | v11 | +| :---------------------- | :----------------------------------- | :----------------------------------------------------------------------------------------------------------------------------- | +| `scss/_breakpoint.scss` | | Moved to `@carbon/grid`, use directly from package or from `@carbon/styles/scss/breakpoint` or `@carbon/react/scss/breakpoint` | +| `scss/_convert.scss` | `$carbon--base-font-size` | Renamed to `$base-font-size` | +| | `@function carbon--rem` | Renamed to `@function rem` | +| | `@function carbon--em` | Renamed to `@function em` | +| `scss/_key-height.scss` | `@function carbon--get-column-width` | Removed | +| | `$carbon--key-height-scales` | Removed | +| | `@function carbon--key-height` | Removed | +| `scss/_mini-unit.scss` | `$carbon--mini-unit-size` | Removed | +| | `@function carbon--mini-units` | Removed | +| `scss/_spacing.scss` | `$carbon--spacing-01` | Removed, use `$spacing-01` instead | +| | `$carbon--spacing-02` | Removed, use `$spacing-02` instead | +| | `$carbon--spacing-03` | Removed, use `$spacing-03` instead | +| | `$carbon--spacing-04` | Removed, use `$spacing-04` instead | +| | `$carbon--spacing-05` | Removed, use `$spacing-05` instead | +| | `$carbon--spacing-06` | Removed, use `$spacing-06` instead | +| | `$carbon--spacing-07` | Removed, use `$spacing-07` instead | +| | `$carbon--spacing-08` | Removed, use `$spacing-08` instead | +| | `$carbon--spacing-09` | Removed, use `$spacing-09` instead | +| | `$carbon--spacing-10` | Removed, use `$spacing-10` instead | +| | `$carbon--spacing-11` | Removed, use `$spacing-11` instead | +| | `$carbon--spacing-12` | Removed, use `$spacing-12` instead | +| | `$carbon--spacing-13` | Removed, use `$spacing-13` instead | +| | `$carbon--spacing` | Removed, use `$spacing` instead | +| | `$spacing-01` | No changes | +| | `$spacing-02` | No changes | +| | `$spacing-03` | No changes | +| | `$spacing-04` | No changes | +| | `$spacing-05` | No changes | +| | `$spacing-06` | No changes | +| | `$spacing-07` | No changes | +| | `$spacing-08` | No changes | +| | `$spacing-09` | No changes | +| | `$spacing-10` | No changes | +| | `$spacing-11` | No changes | +| | `$spacing-12` | No changes | +| | `$spacing-13` | No changes | +| | `$carbon--layout-01` | Removed, use `$spacing-05` instead | +| | `$carbon--layout-02` | Removed, use `$spacing-06` instead | +| | `$carbon--layout-03` | Removed, use `$spacing-07` instead | +| | `$carbon--layout-04` | Removed, use `$spacing-09` instead | +| | `$carbon--layout-05` | Removed, use `$spacing-10` instead | +| | `$carbon--layout-06` | Removed, use `$spacing-12` instead | +| | `$carbon--layout-07` | Removed, use `$spacing-13` instead | +| | `$carbon--layout` | Removed | +| | `$layout-01` | Removed, use `$spacing-05` instead | +| | `$layout-02` | Removed, use `$spacing-06` instead | +| | `$layout-03` | Removed, use `$spacing-07` instead | +| | `$layout-04` | Removed, use `$spacing-09` instead | +| | `$layout-05` | Removed, use `$spacing-10` instead | +| | `$layout-06` | Removed, use `$spacing-12` instead | +| | `$layout-07` | Removed, use `$spacing-13` instead | +| | `$carbon--fluid-spacing-01` | Removed, use `$fluid-spacing-01` instead | +| | `$carbon--fluid-spacing-02` | Removed, use `$fluid-spacing-02` instead | +| | `$carbon--fluid-spacing-03` | Removed, use `$fluid-spacing-03` instead | +| | `$carbon--fluid-spacing-04` | Removed, use `$fluid-spacing-04` instead | +| | `$carbon--fluid-spacing` | Removed, use `$fluid-spacing` instead | +| | `$fluid-spacing-01` | No changes | +| | `$fluid-spacing-02` | No changes | +| | `$fluid-spacing-03` | No changes | +| | `$fluid-spacing-04` | No changes | +| | `$fluid-spacing` | No changes | ## @carbon/motion diff --git a/packages/carbon-react/.storybook/styles.scss b/packages/carbon-react/.storybook/styles.scss index c593088be78e..0686c545f714 100644 --- a/packages/carbon-react/.storybook/styles.scss +++ b/packages/carbon-react/.storybook/styles.scss @@ -6,6 +6,7 @@ // @use '../index.scss' as styles; +@use '../scss/grid/flexbox'; :root { @include styles.theme(styles.$white); diff --git a/packages/carbon-react/__tests__/index-test.js b/packages/carbon-react/__tests__/index-test.js index 2e80dde5771c..b124e5ded612 100644 --- a/packages/carbon-react/__tests__/index-test.js +++ b/packages/carbon-react/__tests__/index-test.js @@ -32,6 +32,7 @@ describe('Carbon Components React', () => { "CodeSnippet", "CodeSnippetSkeleton", "Column", + "ColumnHang", "ComboBox", "ComposedModal", "Content", diff --git a/packages/carbon-react/scss/components/aspect-ratio/_index.scss b/packages/carbon-react/scss/components/aspect-ratio/_index.scss new file mode 100644 index 000000000000..c162f3d35d11 --- /dev/null +++ b/packages/carbon-react/scss/components/aspect-ratio/_index.scss @@ -0,0 +1,9 @@ +// Code generated by @carbon/react. DO NOT EDIT. +// +// Copyright IBM Corp. 2018, 2018 +// +// This source code is licensed under the Apache-2.0 license found in the +// LICENSE file in the root directory of this source tree. +// + +@forward '@carbon/styles/scss/components/aspect-ratio'; diff --git a/packages/carbon-react/scss/grid/_config.scss b/packages/carbon-react/scss/grid/_config.scss new file mode 100644 index 000000000000..03b5b2c0d6fb --- /dev/null +++ b/packages/carbon-react/scss/grid/_config.scss @@ -0,0 +1,9 @@ +// Code generated by @carbon/react. DO NOT EDIT. +// +// Copyright IBM Corp. 2018, 2018 +// +// This source code is licensed under the Apache-2.0 license found in the +// LICENSE file in the root directory of this source tree. +// + +@forward '@carbon/styles/scss/grid/config'; diff --git a/packages/carbon-react/scss/grid/_flexbox.scss b/packages/carbon-react/scss/grid/_flexbox.scss new file mode 100644 index 000000000000..34573cda916f --- /dev/null +++ b/packages/carbon-react/scss/grid/_flexbox.scss @@ -0,0 +1,9 @@ +// Code generated by @carbon/react. DO NOT EDIT. +// +// Copyright IBM Corp. 2018, 2018 +// +// This source code is licensed under the Apache-2.0 license found in the +// LICENSE file in the root directory of this source tree. +// + +@forward '@carbon/styles/scss/grid/flexbox'; diff --git a/packages/carbon-react/scss/_grid.scss b/packages/carbon-react/scss/grid/_index.scss similarity index 100% rename from packages/carbon-react/scss/_grid.scss rename to packages/carbon-react/scss/grid/_index.scss diff --git a/packages/carbon-react/scss/utilities/_convert.scss b/packages/carbon-react/scss/utilities/_convert.scss new file mode 100644 index 000000000000..221775506bd2 --- /dev/null +++ b/packages/carbon-react/scss/utilities/_convert.scss @@ -0,0 +1,9 @@ +// Code generated by @carbon/react. DO NOT EDIT. +// +// Copyright IBM Corp. 2018, 2018 +// +// This source code is licensed under the Apache-2.0 license found in the +// LICENSE file in the root directory of this source tree. +// + +@forward '@carbon/styles/scss/utilities/convert'; diff --git a/packages/carbon-react/scss/utilities/_z-index.scss b/packages/carbon-react/scss/utilities/_z-index.scss new file mode 100644 index 000000000000..8fc3bd0439c1 --- /dev/null +++ b/packages/carbon-react/scss/utilities/_z-index.scss @@ -0,0 +1,9 @@ +// Code generated by @carbon/react. DO NOT EDIT. +// +// Copyright IBM Corp. 2018, 2018 +// +// This source code is licensed under the Apache-2.0 license found in the +// LICENSE file in the root directory of this source tree. +// + +@forward '@carbon/styles/scss/utilities/z-index'; diff --git a/packages/carbon-react/src/index.js b/packages/carbon-react/src/index.js index cfcd0d027063..bf0e11af4ec9 100644 --- a/packages/carbon-react/src/index.js +++ b/packages/carbon-react/src/index.js @@ -187,6 +187,7 @@ export { SideNavSwitcher, Grid, Column, + unstable_ColumnHang as ColumnHang, unstable_FlexGrid as FlexGrid, unstable_useContextMenu, unstable_FeatureFlags as FeatureFlags, diff --git a/packages/carbon-react/tasks/build-styles.js b/packages/carbon-react/tasks/build-styles.js index c5ffb9f1fc67..151d0753c89c 100644 --- a/packages/carbon-react/tasks/build-styles.js +++ b/packages/carbon-react/tasks/build-styles.js @@ -32,8 +32,22 @@ async function build() { filepath: '_feature-flags.scss', }, { - type: 'file', - filepath: '_grid.scss', + type: 'directory', + filepath: 'grid', + files: [ + { + type: 'file', + filepath: '_config.scss', + }, + { + type: 'file', + filepath: '_flexbox.scss', + }, + { + type: 'file', + filepath: '_index.scss', + }, + ], }, { type: 'file', diff --git a/packages/grid/ARCHITECTURE.md b/packages/grid/ARCHITECTURE.md new file mode 100644 index 000000000000..7dd9a803a396 --- /dev/null +++ b/packages/grid/ARCHITECTURE.md @@ -0,0 +1,53 @@ +# Architecture + +> Reference document for the approach of buildling and testing this package. + +## CSS Grid + +The CSS Grid implementation of the IDL Grid is implemented using `display: grid` +but unfortunately is unable to use `grid-gap`, `column-gap`, etc for gutters +because of several requirements we have from the various grid modes we need to +implement. + +In general, our "wide" grid is a grid where each column as 16px of margin on +either side. Our narrow grid, however, will completely drop the leading 16px of +margin. The condensed grid will only have 0.5px of margin on either side of the +column. + +Due to this asymmetry, we need to be able to control the leading and trailing +gutter of each cell (both sides are included for right-to-left layouts). We also +need to offer utilities for hanging content "on the grid" or "on a column" so +that elements like text can appropriately align even if the gutter for the cell +is missing. + +### Testing + +Our CSS Grid implementation has a wide variety of cases to test for, many of +which are in our css-grid preview but should be tested more explicitly in the +future. These ad-hoc tests include verifying: + +- The grid definition itself in `$grid-breakpoints` matches the IDL spec +- The grid has the correct number of columns per brekapoint +- The margin of the grid correctly changes between breakpoints +- The various grid modes work as intended, including + - Wide + - Narrow + - Condensed +- Column span classes have: + - Classes that apply span unconditionally, regardless of breakpoint + - Classes that apply conditionally depending on breakpoint + - Classes that have a span of 0 correctly hide content +- Column offset classes have: + - Classes that apply span unconditionally, regardless of breakpoint + - Classes that apply conditionally depending on breakpoint +- Percent-span column classes have: + - Classes that apply unconditionally + - Classes that apply conditionally depending on breakpoint +- The different grid modes interact cleanly in subgrid and support arbitrary + levels of nesting +- The column hang helper class correctly determines the right amount of margin + to add in such that text aligns to the grid +- The layouts align correctly in a right-to-left orientation +- The "full width" grid allows the grid to span beyond the max-width of the + "max" breakpoint +- The grid classes can be used with custom components to align them to the grid diff --git a/packages/grid/__tests__/__snapshots__/scss-test.js.snap b/packages/grid/__tests__/__snapshots__/scss-test.js.snap index 438ba91b0e7e..f8fe5908e08f 100644 --- a/packages/grid/__tests__/__snapshots__/scss-test.js.snap +++ b/packages/grid/__tests__/__snapshots__/scss-test.js.snap @@ -5,7 +5,7 @@ Array [ "prefix", "flex-grid-columns", "grid-gutter", - "grid-gutter--condensed", + "grid-gutter-condensed", "grid-breakpoints", ] `; diff --git a/packages/grid/__tests__/scss-test.js b/packages/grid/__tests__/scss-test.js index 7909b3af8782..531e916d8a4a 100644 --- a/packages/grid/__tests__/scss-test.js +++ b/packages/grid/__tests__/scss-test.js @@ -15,20 +15,21 @@ const { render } = SassRenderer.create(__dirname); describe('@carbon/grid', () => { test('Public API', async () => { - const { get } = await render(` + const { unwrap } = await render(` @use 'sass:meta'; @use '../index.scss' as grid; $_: get('variables', meta.module-variables('grid')); $_: get('mixins', ( - grid: meta.mixin-exists('css-grid', 'grid'), + css-grid: meta.mixin-exists('css-grid', 'grid'), + flex-grid: meta.mixin-exists('flex-grid', 'grid'), )); `); - const variables = get('variables'); - expect(Object.keys(variables.value)).toMatchSnapshot(); - - const mixins = get('mixins'); - expect(mixins.value.grid).toBe(true); + expect(Object.keys(unwrap('variables'))).toMatchSnapshot(); + expect(unwrap('mixins')).toEqual({ + 'css-grid': true, + 'flex-grid': true, + }); }); }); diff --git a/packages/grid/docs/sass.md b/packages/grid/docs/sass.md new file mode 100644 index 000000000000..8dc5483c9745 --- /dev/null +++ b/packages/grid/docs/sass.md @@ -0,0 +1,74 @@ +# Sass + +> Sass documentation for `@carbon/grid` + +## Usage + +The `@carbon/grid` package ships CSS Grid and Flexbox-based grids, along with +grid utilities, for the IBM Design Language in Sass. You can import and use the +grid by writing the following: + +```scss +@use '@carbon/grid'; + +// Emit all the CSS for the CSS Grid +@include grid.css-grid(); +``` + +With the CSS for CSS Grid, you can create a grid in your HTML by writing the +following: + +```html +
+
Span 4 columns
+
Span 2 columns
+
+``` + +There is also support for responsive classes that are included with the +`css-grid` mixin. These responsive classes allow you to do the following for +each breakpoint: + +- Change how many columns your content spans based on content +- Hide or show content depending on the breakpoint +- Change where your columns start or end + +For a full list of classes available, checkout the [classes](#classes) section +below. + +### Classes + +| Name | Description | +| :----------------------------------------- | :------------------------------------- | +| `cds--css-grid` | Grid class name | +| `cds--css-grid-column` | Column class name | +| `cds--col-span-{0,16}` | Unconditional column span | +| `cds--{sm,md,lg,xlg,max}:col-span-{0,16}` | Responsive column span | +| `cds--col-span-{25,50,75,100}` | Percent column span across breakpoints | +| `cds--col-start-{1,16}` | Unconditional column start | +| `cds--{sm,md,lg,xlg,max}:col-start-{1,16}` | Responsive column start | +| `cds--col-end-{2,17}` | Unconditional column end | +| `cds--{sm,md,lg,xlg,max}:col-end-{2,17}` | Responsive column end | +| `cds--grid-column-hang` | Hang content on a grid column | +| `cds--subgrid` | Specify an element as a subgrid | +| `cds--subgrid--{wide,narrow,condensed}` | Specify the grid mode of subgrid | + +## API + +| Name | Description | Type | Default | +| :----------------------- | :---------------------------------------------------------------------------------- | :---------- | :------------ | +| `css-grid` | Generate the CSS for using the CSS Grid | `@mixin` | | +| `$prefix` | Specify the prefix used for CSS selectors | `String` | `'cds'` | +| `$flex-grid-columns` | Specify the number of columns for the flex grid | `Number` | `16` | +| `$grid-gutter` | Specify the gutter of the grid | `Number` | `32px (2rem)` | +| `$grid-gutter-condensed` | Specify the gutter of the condensed grid | `Number` | `1px` | +| `$grid-breakpoints` | Specify the breakpoints for the grid | `Map` | | +| `breakpoint-next` | Get the value of the next breakpoint | `@function` | | +| `breakpoint-prev` | Get the value of the previous breakpoint | `@function` | | +| `is-smallest-breakpoint` | Check to see if the given breakpoint is the smallest breakpoint | `@function` | | +| `is-largest-breakpoint` | Check to see if the given breakpoint is the largest breakpoint | `@function` | | +| `breakpoint-up` | Generate a media query from the width of the given breakpoint to infinity | `@mixin` | | +| `breakpoint-down` | Generate a media query that applies below the maximum width of the given breakpoint | `@mixin` | | +| `breakpoint-between` | Generate a media query for the range between the lower and upper breakpoints | `@mixin` | | +| `largest-breakpoint` | Generate a media query for the largest breakpoint | `@mixin` | | +| `breakpoint` | Generate a media query for a given breakpoint, alias of `breakpoint-up` | `@mixin` | | diff --git a/packages/grid/examples/css-grid/src/components/Header.js b/packages/grid/examples/css-grid/src/components/Header.js deleted file mode 100644 index 19820abe742c..000000000000 --- a/packages/grid/examples/css-grid/src/components/Header.js +++ /dev/null @@ -1,13 +0,0 @@ -import Link from 'next/link'; - -export function Header({ children, ...rest }) { - return
{children}
; -} - -export function HeaderLabel({ children, ...rest }) { - return ( - - {children} - - ); -} diff --git a/packages/grid/examples/css-grid/src/pages/index.js b/packages/grid/examples/css-grid/src/pages/index.js index 34d5e43e8b4c..74f3bb85d0b3 100644 --- a/packages/grid/examples/css-grid/src/pages/index.js +++ b/packages/grid/examples/css-grid/src/pages/index.js @@ -1,167 +1,463 @@ export default function IndexPage() { return ( <> -

Experimental CSS Grid

-
-

Wide

-
-
-
-
-
-
-
+

CSS Grid

+ +
+
+

RTL layout

+ +
+ + ); +} +function GridExamples() { + return ( + <>
-

Condensed

-
-
-
-
-
+

Wide

+
+
+ Span 4 +
+
+ Span 4 +
+
+ Span 4 +
+
+ Span 4 +
-

Full Width

-
-
-
-
-
+

Narrow

+
+
+
Span 4
+
+
+
Span 4
+
+
+
Span 4
+
+
+
Span 4
+
-

Responsive

-
-
-

Small: Span 2 of 4

-

Medium: Span 4 of 8

-

Large: Span 6 of 16

+

Condensed

+
+
+
Span 4
-
-

Small: Span 2 of 4

-

Medium: Span 2 of 8

-

Large: Span 3 of 16

+
+
Span 4
-
-

Small: Span 0 of 4

-

Medium: Span 2 of 8

-

Large: Span 3 of 16

+
+
Span 4
-
-

Small: Span 0 of 4

-

Medium: Span 0 of 8

-

Large: Span 4 of 16

+
+
Span 4

Subgrid

-
-
-

Small: Span 2 of 4

-

Medium: Span 4 of 8

-

Large: Span 3 of 16

+

Wide base

+
+
+
+
+
+
+
+
+
+
+
+
-
-

Small: Span 2 of 4

-

Medium: Span 4 of 8

-

Large: Span 10 of 16

-
-
-

sm=1

md=1

lg=2

+
+
+
+
I
-
-

sm=1

md=1

lg=2

+
+
I
-
-

sm=0

md=1

lg=1

+
+
I
-
-

sm=0

md=1

lg=1

+
+
I
-
-

sm=0

md=0

lg=1

+
+
I
-
-

sm=0

md=0

lg=1

+
+
I
-
-

sm=0

md=0

lg=1

+
+
I
-
-

sm=0

md=0

lg=1

+
+
I
-
-

Small: Span 0 of 4

-

Medium: Span 0 of 8

-

Large: Span 3 of 16

+
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
-
-
-

Mixed Grid Modes

-
-
-
-
-

wide

+

Narrow base

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
-
-
-
-

condensed

+
+ +

Condensed base

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
-
-
-
-

condensed

+
+ +

Mix-and-match

+
+
+
+
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
+
I
+
I
+
+
+
+
I
+
+
+
I
+
+
+
+
+
+
-
-
-
-

wide

+
+
+
+
+
+
+
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
I
+
+
+
+

Responsive

+
+
+

Small: Span 2 of 4

+

Medium: Span 4 of 8

+

Large: Span 6 of 16

+
+
+

Small: Span 2 of 4

+

Medium: Span 2 of 8

+

Large: Span 3 of 16

+
+
+

Small: Span 0 of 4

+

Medium: Span 2 of 8

+

Large: Span 3 of 16

+
+
+

Small: Span 0 of 4

+

Medium: Span 0 of 8

+

Large: Span 4 of 16

+
+
+
+

Offset

-
-
-
-
-
+
+
+ Start 6 +
+
+ Start 5 +
+
+ Start 4 +
+
+ Start 3 +
+
+ Start 2 +
+
+ Start 1 +
+
+
+
+ column start 2, span 2 +
+
+
+
+ span 2, column end 5 +
+
+
+
+ column start 2, column end 5 +

Auto Columns

-
-
-
-
-
-
-
-
-
+
+
+ Span 100% +
+
+
+
+ Span 75% +
+
+
+
+ Span 50% +
+
+
+
+ Span 25% +
+
+
+
+ 100% at sm, 75% at md, 50% at lg, 25% at xlg +
+
+
+ +
+

Full Width

+
+
+ Span 4 +
+
+ Span 4 +
+
+ Span 4 +
+
+ Span 4 +
+ + ); } + diff --git a/packages/grid/examples/css-grid/src/scss/_reset.scss b/packages/grid/examples/css-grid/src/scss/_reset.scss index 905dcdb2dd54..67418b5d31b4 100644 --- a/packages/grid/examples/css-grid/src/scss/_reset.scss +++ b/packages/grid/examples/css-grid/src/scss/_reset.scss @@ -21,6 +21,6 @@ html, body { body { margin: 0; font-family: 'IBM Plex Mono', monospace; - padding: 1rem; + padding: 0; } diff --git a/packages/grid/examples/css-grid/src/scss/components/_header.scss b/packages/grid/examples/css-grid/src/scss/components/_header.scss deleted file mode 100644 index e92c4872da4d..000000000000 --- a/packages/grid/examples/css-grid/src/scss/components/_header.scss +++ /dev/null @@ -1,26 +0,0 @@ -// -// Copyright IBM Corp. 2018, 2018 -// -// This source code is licensed under the Apache-2.0 license found in the -// LICENSE file in the root directory of this source tree. -// - -@use '@carbon/colors'; -@use '@carbon/grid/scss/modules/css-grid'; - -.cds-header { - --cds-header-background-color: #{colors.$gray-100}; - --cds-header-border-color: #{colors.$gray-80}; - --cds-header-text-color: #{colors.$gray-10}; - --cds-header-icon-color: #{colors.$gray-10}; - - @include css-grid.css-grid(); - background-color: var(--cds-header-background-color); - border-bottom: 1px solid var(--cds-header-border-color); - height: 3rem; -} - -.cds-header-label { - color: var(--cds-header-text-color); - text-decoration: none; -} diff --git a/packages/grid/examples/css-grid/src/styles.scss b/packages/grid/examples/css-grid/src/styles.scss index d334d3f33687..aaa9e567161d 100644 --- a/packages/grid/examples/css-grid/src/styles.scss +++ b/packages/grid/examples/css-grid/src/styles.scss @@ -5,45 +5,87 @@ // LICENSE file in the root directory of this source tree. // +@use '@carbon/colors'; +@use '@carbon/grid'; @use 'scss/reset'; -@use '@carbon/grid/scss/modules/css-grid'; -@use 'scss/components'; -@import '@carbon/layout/scss/spacing'; -@import '@carbon/type/scss/styles'; -@import '~carbon-components/scss/globals/scss/vars'; +@include grid.css-grid(); // Grid -.bx--css-grid { - background-color: $blue-20; - outline: 1px dashed $blue-40; +.cds--css-grid { + background-color: colors.$blue-20; + outline: 1px dashed colors.$blue-40; } -.bx--css-grid.bx--css-grid--condensed { - background-color: $purple-20; - outline: 1px dashed $purple-40; +.cds--css-grid.cds--css-grid--condensed { + background-color: colors.$purple-20; + outline: 1px dashed colors.$purple-40; } -// Only use background for subgrid example, not other subgrids in "mixed modes" example or others -.bx--subgrid.example { - background-color: $green-20; +.cds--css-grid, .cds--subgrid--wide { + --grid-mode-color: #97C1FF; +} + +.cds--css-grid--narrow, +.cds--subgrid--narrow { + --grid-mode-color: #20D5D2; + background: colors.$green-10; +} + +.cds--css-grid--condensed, +.cds--subgrid--condensed { + --grid-mode-color: #BB8EFF; + background: colors.$purple-10; } // Columns -.bx--css-grid > [class*='col'], -.bx--subgrid > [class*='col'] { +.cds--css-grid > [class*='col'], +.cds--subgrid > [class*='col'] { min-height: 80px; } -.bx--css-grid > [class*='col'] { - background: $blue-10; +.cds--subgrid { + outline: 1px solid black; + padding-top: 2rem; + padding-bottom: 2rem; + position: relative; + background: #EEF4FF; +} + +.cds--subgrid--narrow { + background: #D6F9F9; +} + +.cds--subgrid--condensed { + background: #F7F2FF; +} + +.cds--subgrid::before { + position: absolute; + inset-block-start: 0; + inset-inline-start: 0; + height: 1.25rem; + display: block; + content: 'subgrid'; + background: var(--grid-mode-color, #97C1FF); + color: black; + font-size: 12px; + padding: 0 0.25rem; +} + +.cds--css-grid-column { + --border-color: #97C1FF; + + background: white; + box-shadow: 0 0 0 1px var(--border-color); } -.bx--subgrid > [class*='col'] { - background: $green-10; - outline: 1px dashed $green-30; +.cds--css-grid--narrow .cds--css-grid-column, +.cds--subgrid--narrow .cds--css-grid-column { + --border-color: #20D5D2; } -.bx--css-grid.bx--css-grid--condensed > [class*='col'] { - background: $purple-10; +.cds--css-grid--condensed .cds--css-grid-column, +.cds--subgrid--condensed .cds--css-grid-column { + --border-color: #BB8EFF; } diff --git a/packages/grid/index.scss b/packages/grid/index.scss index 8b6f9c3cb551..493712f54a0d 100644 --- a/packages/grid/index.scss +++ b/packages/grid/index.scss @@ -5,10 +5,7 @@ // LICENSE file in the root directory of this source tree. // -@forward 'scss/modules/config' with ( - $prefix: 'bx' !default, - $flex-grid-columns: 16 !default, -); +@forward 'scss/modules/config'; @forward 'scss/modules/breakpoint'; @forward 'scss/modules/css-grid'; @forward 'scss/modules/flex-grid'; diff --git a/packages/grid/scss/modules/_breakpoint.scss b/packages/grid/scss/modules/_breakpoint.scss index d21dcf048b66..5723834df205 100644 --- a/packages/grid/scss/modules/_breakpoint.scss +++ b/packages/grid/scss/modules/_breakpoint.scss @@ -5,25 +5,11 @@ // LICENSE file in the root directory of this source tree. // -// https://github.com/twbs/bootstrap/blob/v4-dev/scss/mixins/_breakpoints.scss @use 'sass:list'; @use 'sass:map'; @use 'sass:meta'; @use '@carbon/layout/scss/modules/convert'; - -/// Map deep get -/// @author Hugo Giraudel -/// @access public -/// @param {Map} $map - Map -/// @param {Arglist} $keys - Key chain -/// @return {*} Desired value -/// @group @carbon/layout -@function map-deep-get($map, $keys...) { - @each $key in $keys { - $map: map.get($map, $key); - } - @return $map; -} +@use 'config' as *; /// Provide a map and index, and get back the relevant key value /// @access public @@ -31,65 +17,11 @@ /// @param {Integer} $index - Key chain /// @return {String} Desired value /// @group @carbon/layout -@function key-by-index($map, $index) { +@function -key-by-index($map, $index) { $keys: map.keys($map); @return nth($keys, $index); } -/// Pass in a map, and get the last one in the list back -/// @access public -/// @param {Map} $map - Map -/// @return {*} Desired value -/// @group @carbon/layout -@function last-map-item($map) { - $total-length: list.length($map); - @return map-get($map, key-by-index($map, $total-length)); -} - -/// Carbon gutter size in rem -/// @type Number -/// @access public -/// @group @carbon/layout -$grid-gutter: convert.rem(32px); - -/// Carbon condensed gutter size in rem -/// @type Number -/// @access public -/// @group @carbon/layout -$grid-gutter--condensed: convert.rem(1px); - -// Initial map of our breakpoints and their values -/// @type Map -/// @access public -/// @group @carbon/layout -$grid-breakpoints: ( - sm: ( - columns: 4, - margin: 0, - width: convert.rem(320px), - ), - md: ( - columns: 8, - margin: convert.rem(16px), - width: convert.rem(672px), - ), - lg: ( - columns: 16, - margin: convert.rem(16px), - width: convert.rem(1056px), - ), - xlg: ( - columns: 16, - margin: convert.rem(16px), - width: convert.rem(1312px), - ), - max: ( - columns: 16, - margin: convert.rem(24px), - width: convert.rem(1584px), - ), -) !default; - /// Get the value of the next breakpoint, or null for the last breakpoint /// @param {String} $name - The name of the breakpoint /// @param {Map} $breakpoints [$grid-breakpoints] - A map of breakpoints where the key is the name of the breakpoint and the value is the values for the breakpoint @@ -145,7 +77,7 @@ $grid-breakpoints: ( /// @group @carbon/layout @function largest-breakpoint-name($breakpoints: $grid-breakpoints) { $total-breakpoints: list.length($breakpoints); - @return key-by-index($breakpoints, $total-breakpoints); + @return -key-by-index($breakpoints, $total-breakpoints); } /// Get the infix for a given breakpoint in a list of breakpoints. Useful for generating the size part in a selector, for example: `.prefix--col-sm-2`. diff --git a/packages/grid/scss/modules/_config.scss b/packages/grid/scss/modules/_config.scss index f7f9f748cb01..00efeb0c459d 100644 --- a/packages/grid/scss/modules/_config.scss +++ b/packages/grid/scss/modules/_config.scss @@ -5,14 +5,90 @@ // LICENSE file in the root directory of this source tree. // +@use 'sass:map'; +@use '@carbon/layout/scss/modules/convert'; + /// Namespace prefix /// @type String /// @access public /// @group @carbon/grid -$prefix: 'bx' !default; +$prefix: 'cds' !default; /// Total columns used in the flex grid /// @type Number /// @access public /// @group @carbon/grid $flex-grid-columns: 16 !default; + +/// Carbon gutter size in rem +/// @type Number +/// @access public +/// @group @carbon/layout +$grid-gutter: convert.rem(32px) !default; + +/// Carbon condensed gutter size in rem +/// @type Number +/// @access public +/// @group @carbon/layout +$grid-gutter-condensed: convert.rem(1px) !default; + +// Initial map of our breakpoints and their values +/// @type Map +/// @access public +/// @group @carbon/layout +$grid-breakpoints: ( + sm: ( + columns: 4, + margin: 0, + width: convert.rem(320px), + ), + md: ( + columns: 8, + margin: convert.rem(16px), + width: convert.rem(672px), + ), + lg: ( + columns: 16, + margin: convert.rem(16px), + width: convert.rem(1056px), + ), + xlg: ( + columns: 16, + margin: convert.rem(16px), + width: convert.rem(1312px), + ), + max: ( + columns: 16, + margin: convert.rem(24px), + width: convert.rem(1584px), + ), +) !default; + +@if $flex-grid-columns == 12 { + $grid-breakpoints: map.merge( + $grid-breakpoints, + ( + lg: + map.merge( + map.get($grid-breakpoints, lg), + ( + columns: 12, + ) + ), + xlg: + map.merge( + map.get($grid-breakpoints, xlg), + ( + columns: 12, + ) + ), + max: + map.merge( + map.get($grid-breakpoints, max), + ( + columns: 12, + ) + ), + ) + ); +} diff --git a/packages/grid/scss/modules/_css-grid.scss b/packages/grid/scss/modules/_css-grid.scss index b2eaf5e5e0a4..086b057c2911 100644 --- a/packages/grid/scss/modules/_css-grid.scss +++ b/packages/grid/scss/modules/_css-grid.scss @@ -7,341 +7,464 @@ @use 'sass:list'; @use 'sass:map'; -@use "sass:math"; +@use 'sass:math'; @use 'sass:meta'; @use 'config' as *; @use 'breakpoint' as *; -@mixin css-grid() { - display: grid; - max-width: 99rem; - padding-right: calc(var(--cds-grid-margin) + var(--cds-grid-gutter) / 2); - padding-left: calc(var(--cds-grid-margin) + var(--cds-grid-gutter) / 2); - margin-right: auto; - margin-left: auto; - column-gap: var(--cds-grid-gutter); - grid-template-columns: repeat( - var(--cds-grid-columns), - minmax(0, var(--cds-grid-column-size)) - ); -} - -@mixin subgrid() { - display: grid; - column-gap: var(--cds-grid-gutter); - grid-template-columns: repeat( - var(--cds-grid-columns), - minmax(0, var(--cds-grid-column-size)) - ); -} - -:root { - --cds-grid-columns: 4; - --cds-grid-column-size: 1fr; - --cds-grid-gutter: 2rem; - // Used to configure appropriate margins for condensed subgrids inside wide grids - --cds-grid-gutter-wide: 2rem; - --cds-grid-hang: 1rem; - --cds-grid-margin: 0; - - @include breakpoint(md) { - --cds-grid-columns: 8; - --cds-grid-margin: 1rem; +/// Emit all the styles related to the CSS Grid, these include: +/// - The base grid class +/// - The various grid modes +/// - The ability to specifiy column span +/// - The ability to specifiy column start,end position +/// - Support for subgrid +/// - Support for hanging content on a grid column +/// - Support for controlling grid gutter on a cell basis +/// - Support for specifying content alignment +/// +/// +/// In general, this mixin is structured in a way to emit the fewest CSS styles +/// as possible. To do this, we will (as much as possible) not emit a value if +/// one is already defined at a previous breakpoint. +/// +/// In addition, this mixin will break down emitting styles into several stages: +/// 1. Emit styles for the smallest breakpoint without any breakpoint. By +/// default, these styles will be on +/// 2. For every other breakpoint, wrap it in a breakpoint so that it only is +/// triggered when that breakpoint is applied +/// 3. In situations where it is appropriate, we also will emit "unconditional" +/// selectors that will always apply. For example, if you wanted a column to +/// always span four columns +@mixin css-grid($breakpoints: $grid-breakpoints) { + /// The :root selector is responsible for setting several top-level CSS Custom + /// Properties, including the overall grid gutter, grid column count, and grid + /// margin + :root { + --cds-grid-gutter: #{$grid-gutter}; + + // Iterate through the grid breakpoints and only emit the grid-columns and + // grid-margin CSS Custom Properties if they've changed from the previous + // breakpoint. By default, we emit the smallest breakpoint values on the + // :root selector + @each $key, $value in $breakpoints { + @if is-smallest-breakpoint($key, $breakpoints) { + --cds-grid-columns: #{get-column-count($breakpoints, $key)}; + --cds-grid-margin: #{get-margin($breakpoints, $key)}; + } @else { + $previous-breakpoint: breakpoint-prev($key, $breakpoints); + $changes: (); + + @if get-column-count($breakpoints, $key) != + get-column-count($breakpoints, $previous-breakpoint) + { + $changes: map.set( + $changes, + grid-columns, + get-column-count($breakpoints, $key) + ); + } + + @if get-margin($breakpoints, $key) != + get-margin($breakpoints, $previous-breakpoint) + { + $changes: map.set( + $changes, + grid-margin, + get-margin($breakpoints, $key) + ); + } + + @include breakpoint($key) { + @each $name, $value in $changes { + --cds-#{$name}: #{$value}; + } + } + } + } } - @include breakpoint(lg) { - --cds-grid-columns: 16; + // ----------------------------------------------------------------------------- + // Base CSS Grid + // ----------------------------------------------------------------------------- + .#{$prefix}--css-grid { + --cds-grid-gutter-start: calc(var(--cds-grid-gutter) / 2); + --cds-grid-gutter-end: calc(var(--cds-grid-gutter) / 2); + // We split out a separate "column hang" since "gutter-start" is set + // dynamically and could be 0 + --cds-grid-column-hang: calc(var(--cds-grid-gutter) / 2); + + display: grid; + max-width: get-grid-width( + $breakpoints, + largest-breakpoint-name($breakpoints) + ); + padding-right: var(--cds-grid-margin); + padding-left: var(--cds-grid-margin); + margin-right: auto; + margin-left: auto; + grid-template-columns: repeat(var(--cds-grid-columns), minmax(0, 1fr)); } - @include breakpoint(max) { - --cds-grid-margin: 1.5rem; + // ----------------------------------------------------------------------------- + // Full width + // ----------------------------------------------------------------------------- + .#{$prefix}--css-grid--full-width { + max-width: 100%; } -} -.#{$prefix}--css-grid { - @include css-grid(); -} - -.#{$prefix}--css-grid--12 { - @include css-grid(); - @include breakpoint(lg) { - --cds-grid-columns: 12; + // ----------------------------------------------------------------------------- + // Column + // ----------------------------------------------------------------------------- + + // Add gutter to columns in a CSS Grid. Unfortunately, we cannot use + // `grid-gap`, `column-gap`, etc. as we need to conditionally remove leading + // and trailing gutter from a column. + .#{$prefix}--css-grid-column { + // grid-mode-start, grid-mode-end are meant to capture the "grid settings" + // so that subgrids can correctly fit in parent grids by reverting the + // grid's outer padding + --cds-grid-mode-start: var(--cds-grid-gutter-start); + --cds-grid-mode-end: var(--cds-grid-gutter-end); + + margin-right: var(--cds-grid-gutter-end); + margin-left: var(--cds-grid-gutter-start); + + [dir='rtl'] & { + margin-right: var(--cds-grid-gutter-start); + margin-left: var(--cds-grid-gutter-end); + } } -} - -.#{$prefix}--subgrid { - @include subgrid(); -} -.#{$prefix}--subgrid[class*='col'] { - display: grid; -} - -// ----------------------------------------------------------------------------- -// Condensed -// ----------------------------------------------------------------------------- -.#{$prefix}--css-grid--condensed { - --cds-grid-gutter: 1px; - - column-gap: var(--cds-grid-gutter); - row-gap: var(--cds-grid-gutter); -} + // ----------------------------------------------------------------------------- + // Grid modes + // ----------------------------------------------------------------------------- -// condensed subgrid inside wide -.#{$prefix}--css-grid .bx--subgrid.#{$prefix}--css-grid--condensed { - margin-right: calc((var(--cds-grid-gutter-wide) / 2) * -1); - margin-left: calc((var(--cds-grid-gutter-wide) / 2) * -1); -} - -// ----------------------------------------------------------------------------- -// No Gutter -// ----------------------------------------------------------------------------- -.#{$prefix}--css-grid--no-gutter { - // This is set to 0px (versus 0) so that the calc expression for padding for - // a grid container works as expected. Without the unit, the calc() will - // result in a value of 0. - // stylelint-disable-next-line length-zero-no-unit - --cds-grid-gutter: 0px; - - column-gap: var(--cds-grid-gutter); -} - -// ----------------------------------------------------------------------------- -// Full width -// ----------------------------------------------------------------------------- -.#{$prefix}--css-grid--full-width { - max-width: 100%; -} - -// ----------------------------------------------------------------------------- -// Column span -// ----------------------------------------------------------------------------- -@mixin -column-span($i) { - @if $i == 0 { - display: none; - } @else { - --cds-grid-columns: #{$i}; + // Narrow + .#{$prefix}--css-grid--narrow { + --cds-grid-gutter-start: 0; + } - display: block; - grid-column: span $i / span $i; + // Condensed + .#{$prefix}--css-grid--condensed { + --cds-grid-gutter: #{$grid-gutter-condensed}; + --cds-grid-column-hang: #{math.div($grid-gutter, 2) - + math.div($grid-gutter-condensed, 2)}; } -} -@for $i from 0 through 16 { - .#{$prefix}--col-span-#{$i} { - @include -column-span($i); + // ----------------------------------------------------------------------------- + // Subgrid + // ----------------------------------------------------------------------------- + .#{$prefix}--subgrid { + display: grid; + margin-right: calc(var(--cds-grid-mode-end) * -1); + margin-left: calc(var(--cds-grid-mode-start) * -1); + grid-template-columns: repeat(var(--cds-grid-columns), minmax(0, 1fr)); + + [dir='rtl'] & { + margin-right: calc(var(--cds-grid-mode-start) * -1); + margin-left: calc(var(--cds-grid-mode-end) * -1); + } } -} -.#{$prefix}--col-span-auto { - grid-column: auto; -} + // Support the grid modes in subgrids + .#{$prefix}--subgrid--wide { + --cds-grid-gutter-start: #{math.div($grid-gutter, 2)}; + --cds-grid-gutter-end: #{math.div($grid-gutter, 2)}; + --cds-grid-column-hang: 0; + } -.#{$prefix}--col-span-100 { - grid-column: 1 / -1; -} + .#{$prefix}--subgrid--narrow { + --cds-grid-gutter-start: 0; + --cds-grid-gutter-end: #{math.div($grid-gutter, 2)}; + --cds-grid-column-hang: #{math.div($grid-gutter, 2)}; + } -.#{$prefix}--col-span-25 { - --cds-grid-columns: 1; + .#{$prefix}--subgrid--condensed { + --cds-grid-gutter-start: #{math.div($grid-gutter-condensed, 2)}; + --cds-grid-gutter-end: #{math.div($grid-gutter-condensed, 2)}; + --cds-grid-column-hang: #{math.div($grid-gutter, 2) - + math.div($grid-gutter-condensed, 2)}; + } - grid-column: span 1; + // ----------------------------------------------------------------------------- + // Column hang + // ----------------------------------------------------------------------------- - @include breakpoint(md) { - --cds-grid-columns: 2; + // Helper class to allow for text alignment in columns where the leading + // gutter is missing (like narrow) or is reduced (like in condensed). + .#{$prefix}--grid-column-hang { + margin-left: var(--cds-grid-column-hang); - grid-column: span 2; + [dir='rtl'] & { + margin-right: var(--cds-grid-column-hang); + margin-left: initial; + } } - @include breakpoint(lg) { - --cds-grid-columns: 4; + // ----------------------------------------------------------------------------- + // Column span + // ----------------------------------------------------------------------------- - grid-column: span 4; + // Generate col-span-{0-16} classes which unconditionally set column span + // regardless of breakpoint + @for $i from 0 through get-grid-columns($breakpoints) { + .#{$prefix}--col-span-#{$i} { + @include -column-span($i); + } } -} -.#{$prefix}--col-span-50 { - --cds-grid-columns: 2; - - grid-column: span 2; + // Responsive column span + @each $name, $value in $breakpoints { + // Column span per breakpoint + @for $i from 0 through get-column-count($breakpoints, $name) { + @if is-smallest-breakpoint($name, $breakpoints) { + .#{$prefix}--#{$name}\:col-span-#{$i} { + @include -column-span($i); + } + } @else { + @include breakpoint($name) { + .#{$prefix}--#{$name}\:col-span-#{$i} { + @include -column-span($i); + } + } + } + } - @include breakpoint(md) { - --cds-grid-columns: 4; + // Percent column span per breakpoint + @if is-smallest-breakpoint($name, $breakpoints) { + .#{$prefix}--#{$name}\:col-span-auto { + grid-column: auto; + } - grid-column: span 4; - } + .#{$prefix}--#{$name}\:col-span-100 { + grid-column: 1 / -1; + } - @include breakpoint(lg) { - --cds-grid-columns: 8; + $columns: get-column-count($breakpoints, $name); - grid-column: span 8; - } -} + .#{$prefix}--#{$name}\:col-span-75 { + $span: $columns * 0.75; + --cds-grid-columns: #{$span}; -.#{$prefix}--col-span-75 { - --cds-grid-columns: 3; + grid-column: span #{$span} / span #{$span}; + } - grid-column: span 3; + .#{$prefix}--#{$name}\:col-span-50 { + $span: $columns * 0.5; + --cds-grid-columns: #{$span}; - @include breakpoint(md) { - --cds-grid-columns: 6; + grid-column: span #{$span} / span #{$span}; + } - grid-column: span 6; - } + .#{$prefix}--#{$name}\:col-span-25 { + $span: $columns * 0.25; + --cds-grid-columns: #{$span}; - @include breakpoint(lg) { - --cds-grid-columns: 12; + grid-column: span #{$span} / span #{$span}; + } + } @else { + @include breakpoint($name) { + .#{$prefix}--#{$name}\:col-span-auto { + grid-column: auto; + } - grid-column: span 12; - } -} + .#{$prefix}--#{$name}\:col-span-100 { + grid-column: 1 / -1; + } -@each $name, $value in $grid-breakpoints { - $columns: map.get($value, columns); + $columns: get-column-count($breakpoints, $name); - @include breakpoint($name) { - @for $i from 0 through $columns { - .#{$prefix}--#{$name}\:col-span-#{$i} { - @include -column-span($i); - } - } + .#{$prefix}--#{$name}\:col-span-75 { + $span: $columns * 0.75; + --cds-grid-columns: #{$span}; - .#{$prefix}--#{$name}\:col-span-auto { - grid-column: auto; - } + grid-column: span #{$span} / span #{$span}; + } - .#{$prefix}--#{$name}\:col-span-100 { - grid-column: 1 / -1; - } + .#{$prefix}--#{$name}\:col-span-50 { + $span: $columns * 0.5; + --cds-grid-columns: #{$span}; - $quarterGridColumns: math.div($columns, 4); + grid-column: span #{$span} / span #{$span}; + } - .#{$prefix}--#{$name}\:col-span-75 { - $calc: $quarterGridColumns * 3; - --cds-grid-columns: #{$calc}; + .#{$prefix}--#{$name}\:col-span-25 { + $span: $columns * 0.25; + --cds-grid-columns: #{$span}; - grid-column: span $calc / span $calc; + grid-column: span #{$span} / span #{$span}; + } + } } + } - .#{$prefix}--#{$name}\:col-span-50 { - $calc: $quarterGridColumns * 2; - --cds-grid-columns: #{$calc}; + // ----------------------------------------------------------------------------- + // Column percent span + // ----------------------------------------------------------------------------- + .#{$prefix}--col-span-auto { + grid-column: auto; + } - grid-column: span $calc / span $calc; - } + .#{$prefix}--col-span-100 { + grid-column: 1 / -1; + } - .#{$prefix}--#{$name}\:col-span-25 { - --cds-grid-columns: #{$quarterGridColumns}; + .#{$prefix}--col-span-75 { + @include -percent-column-span($breakpoints, 0.75); + } - grid-column: span $quarterGridColumns / span $quarterGridColumns; - } + .#{$prefix}--col-span-50 { + @include -percent-column-span($breakpoints, 0.5); } -} -// ----------------------------------------------------------------------------- -// Column offset -// ----------------------------------------------------------------------------- -@for $i from 1 through 17 { - .#{$prefix}--col-start-#{$i} { - grid-column-start: $i; + .#{$prefix}--col-span-25 { + @include -percent-column-span($breakpoints, 0.25); } - .#{$prefix}--col-end-#{$i} { - grid-column-start: $i; + // ----------------------------------------------------------------------------- + // Column offset + // ----------------------------------------------------------------------------- + // Unconditional column start + // Note: we start at 1 and end at column-count to match grid lines. We do not + // start at column-count + 1 since starting at the end of the grid would mean + // a column would have no width available + @for $i from 1 through get-grid-columns($breakpoints) { + .#{$prefix}--col-start-#{$i} { + grid-column-start: $i; + } } -} -.#{$prefix}--col-start-auto { - grid-column-start: auto; -} + // Unconditional column end + // Note: we start at 2 since a column ending at line 1 would have no width. We + // end at column-count + 1 since grid lines start at 1 + @for $i from 2 through get-grid-columns($breakpoints) + 1 { + .#{$prefix}--col-end-#{$i} { + grid-column-end: $i; + } + } -.#{$prefix}--col-end-auto { - grid-column-start: end; -} + .#{$prefix}--col-start-auto { + grid-column-start: auto; + } -@each $name, $value in $grid-breakpoints { - $columns: map.get($value, columns); + .#{$prefix}--col-end-auto { + grid-column-end: auto; + } - @include breakpoint($name) { - // The `grid-column-start` property is *not* inclusive. - // It starts the column *at* the column, not *on* the column. We must - // ensure that there is one additional class available for each breakpoint. - @for $i from 1 through $columns + 1 { - .#{$prefix}--#{$name}\:col-start-#{$i} { - grid-column-start: $i; + // Responsive column start, end + @each $name, $value in $breakpoints { + @if is-smallest-breakpoint($name, $breakpoints) { + // Responsive column start + @for $i from 1 through get-grid-columns($breakpoints) { + .#{$prefix}--#{$name}\:col-start-#{$i} { + grid-column-start: $i; + } } - .#{$prefix}--#{$name}\:col-end-#{$i} { - grid-column-end: $i; + // Responsive column end + @for $i from 2 through get-grid-columns($breakpoints) + 1 { + .#{$prefix}--#{$name}\:col-end-#{$i} { + grid-column-end: $i; + } } - } - .#{$prefix}--#{$name}\:col-start-auto { - grid-column-start: auto; - } + .#{$prefix}--#{$name}\:col-start-auto { + grid-column-start: auto; + } - .#{$prefix}--#{$name}\:col-end-auto { - grid-column-start: end; + .#{$prefix}--#{$name}\:col-end-auto { + grid-column-end: auto; + } + } @else { + @include breakpoint($name) { + // Responsive column start + @for $i from 1 through get-grid-columns($breakpoints) { + .#{$prefix}--#{$name}\:col-start-#{$i} { + grid-column-start: $i; + } + } + + // Responsive column end + @for $i from 2 through get-grid-columns($breakpoints) + 1 { + .#{$prefix}--#{$name}\:col-end-#{$i} { + grid-column-end: $i; + } + } + + .#{$prefix}--#{$name}\:col-start-auto { + grid-column-start: auto; + } + + .#{$prefix}--#{$name}\:col-end-auto { + grid-column-end: auto; + } + } } } } -// ----------------------------------------------------------------------------- -// Hang -// ----------------------------------------------------------------------------- -.#{$prefix}--hang { - padding-left: var(--cds-grid-hang); -} - -// ----------------------------------------------------------------------------- -// Column gutter -// ----------------------------------------------------------------------------- -.#{$prefix}--gutter { - padding-right: var(--cds-grid-hang); - padding-left: var(--cds-grid-hang); -} - -.#{$prefix}--gutter-start { - padding-left: var(--cds-grid-hang); -} +/// Generate the styles for a grid column +@mixin -column-span($i) { + @if $i == 0 { + display: none; + } @else { + --cds-grid-columns: #{$i}; -[dir='rtl'] .#{$prefix}--gutter-start { - padding-right: var(--cds-grid-hang); + display: block; + grid-column: span $i / span $i; + } } -.#{$prefix}--gutter-end { - padding-right: var(--cds-grid-hang); -} +/// Generate the styles for an unconditional class that represents a percent +/// span of a grid +@mixin -percent-column-span($breakpoints, $percent) { + @each $key, $value in $breakpoints { + $columns: get-column-count($breakpoints, $key); + $span: $columns * $percent; -[dir='rtl'] .#{$prefix}--gutter-end { - padding-left: var(--cds-grid-hang); -} + @if is-smallest-breakpoint($key, $breakpoints) { + --cds-grid-columns: #{$span}; -// ----------------------------------------------------------------------------- -// Utilities -// ----------------------------------------------------------------------------- + grid-column: span #{$span} / span #{$span}; + } @else { + $previous-breakpoint: breakpoint-prev($key, $breakpoints); + $previous-column-count: get-column-count( + $breakpoints, + $previous-breakpoint + ); + $previous-span: $previous-column-count * $percent; -/// Justify items -.#{$prefix}--justify-items-start { - justify-items: start; -} + @if $span != $previous-span { + @include breakpoint($key) { + --cds-grid-columns: #{$span}; -.#{$prefix}--justify-items-end { - justify-items: end; + grid-column: span #{$span} / span #{$span}; + } + } + } + } } -.#{$prefix}--justify-items-center { - justify-items: center; +/// Get the grid width for a specific breakpoint name +@function get-grid-width($breakpoints, $breakpoint) { + @return map.get(map.get($breakpoints, $breakpoint), width); } -/// Align items -.#{$prefix}--align-items-start { - align-items: start; +/// Get the grid column count for a specific breakpoint name +@function get-column-count($breakpoints, $breakpoint) { + @return map.get(map.get($breakpoints, $breakpoint), columns); } -.#{$prefix}--align-items-end { - align-items: end; +/// Get the grid margin for a specific breakpoint name +@function get-margin($breakpoints, $breakpoint) { + $value: map.get(map.get($breakpoints, $breakpoint), margin); + @if $value == 0 { + @return 0; + } + @return $value; } -.#{$prefix}--align-items-center { - align-items: center; +/// Return the largest column count from a set of breakpoints +@function get-grid-columns($breakpoints) { + @return get-column-count($breakpoints, largest-breakpoint-name($breakpoints)); } diff --git a/packages/grid/scss/modules/_flex-grid.scss b/packages/grid/scss/modules/_flex-grid.scss index c3bb85125c8c..f9a5d825ed30 100644 --- a/packages/grid/scss/modules/_flex-grid.scss +++ b/packages/grid/scss/modules/_flex-grid.scss @@ -9,6 +9,7 @@ // and often derived from, bootstrap: // https://github.com/twbs/bootstrap/blob/v4-dev/scss/mixins/_grid.scss +@use 'sass:list'; @use 'sass:meta'; @use 'sass:math'; @use 'sass:map'; @@ -24,12 +25,12 @@ /// for setting width and default gutters when a column's breakpoint has not been /// hit yet. /// @param {Number} $gutter [$grid-gutter] - The gutter for the grid system -/// @param {Number} $collapsed-gutter [$grid-gutter--condensed] - The condensed mode gutter +/// @param {Number} $collapsed-gutter [$grid-gutter-condensed] - The condensed mode gutter /// @access private /// @group @carbon/grid @mixin -make-col-ready( $gutter: $grid-gutter, - $condensed-gutter: $grid-gutter--condensed + $condensed-gutter: $grid-gutter-condensed ) { // Prevent columns from becoming too narrow when at smaller grid tiers by // always setting `width: 100%;`. This works because we use `flex` values @@ -254,7 +255,7 @@ margin-right: auto; margin-left: auto; - @include -set-largest-breakpoint(); + @include -set-largest-breakpoint($breakpoints); @each $name, $value in $breakpoints { $prev-breakpoint: map.get($breakpoints, breakpoint-prev($name)); @@ -282,7 +283,7 @@ /// @access private /// @group @carbon/grid @mixin -set-largest-breakpoint($breakpoints: $grid-breakpoints) { - $largest-breakpoint: last-map-item($breakpoints); + $largest-breakpoint: -last-map-item($breakpoints); max-width: map.get($largest-breakpoint, 'width'); } @@ -299,16 +300,37 @@ } } +/// Pass in a map, and get the last one in the list back +/// @access public +/// @param {Map} $map - Map +/// @return {*} Desired value +/// @group @carbon/layout +@function -last-map-item($map) { + $total-length: list.length($map); + @return map-get($map, -key-by-index($map, $total-length)); +} + +/// Provide a map and index, and get back the relevant key value +/// @access public +/// @param {Map} $map - Map +/// @param {Integer} $index - Key chain +/// @return {String} Desired value +/// @group @carbon/layout +@function -key-by-index($map, $index) { + $keys: map.keys($map); + @return nth($keys, $index); +} + /// Generate the CSS for a grid for the given breakpoints and gutters /// @param {Map} $breakpoints [$grid-breakpoints] - The default breakpoints /// @param {Number} $grid-gutter [$grid-gutter] - The default gutters -/// @param {Number} $condensed-gutter [$grid-gutter--condensed] - The condensed mode gutter +/// @param {Number} $condensed-gutter [$grid-gutter-condensed] - The condensed mode gutter /// @access public /// @group @carbon/grid @mixin flex-grid( $breakpoints: $grid-breakpoints, $grid-gutter: $grid-gutter, - $condensed-gutter: $grid-gutter--condensed + $condensed-gutter: $grid-gutter-condensed ) { .#{$prefix}--grid { @include -make-container($breakpoints); @@ -339,36 +361,3 @@ @include -no-gutter(); @include -hang($grid-gutter); } - -@if $flex-grid-columns == 12 { - $flex-12-column-grid: map.merge( - $grid-breakpoints, - ( - lg: - map.merge( - map.get($grid-breakpoints, lg), - ( - columns: 12, - ) - ), - xlg: - map.merge( - map.get($grid-breakpoints, xlg), - ( - columns: 12, - ) - ), - max: - map.merge( - map.get($grid-breakpoints, max), - ( - columns: 12, - ) - ), - ) - ); - - @include flex-grid($breakpoints: $flex-12-column-grid); -} @else { - @include flex-grid(); -} diff --git a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap index 945a57d6b31b..3150a9863234 100644 --- a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap +++ b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap @@ -7994,6 +7994,29 @@ Map { }, }, }, + "unstable_ColumnHang" => Object { + "propTypes": Object { + "as": Object { + "args": Array [ + Array [ + Object { + "type": "string", + }, + Object { + "type": "elementType", + }, + ], + ], + "type": "oneOfType", + }, + "children": Object { + "type": "node", + }, + "className": Object { + "type": "string", + }, + }, + }, "unstable_DefinitionTooltip" => Object { "propTypes": Object { "align": Object { diff --git a/packages/react/src/__tests__/index-test.js b/packages/react/src/__tests__/index-test.js index 5b1b417106c7..c283775ad7bb 100644 --- a/packages/react/src/__tests__/index-test.js +++ b/packages/react/src/__tests__/index-test.js @@ -200,6 +200,7 @@ describe('Carbon Components React', () => { "TooltipDefinition", "TooltipIcon", "UnorderedList", + "unstable_ColumnHang", "unstable_DefinitionTooltip", "unstable_FeatureFlags", "unstable_FlexGrid", diff --git a/packages/react/src/components/Grid/CSSGrid.js b/packages/react/src/components/Grid/CSSGrid.js index c8d90de0ad8d..4cf48628c010 100644 --- a/packages/react/src/components/Grid/CSSGrid.js +++ b/packages/react/src/components/Grid/CSSGrid.js @@ -13,23 +13,41 @@ import { GridSettings, useGridSettings } from './GridContext'; function CSSGrid({ as: BaseComponent = 'div', + children, + className: customClassName, condensed = false, fullWidth = false, - columns = 16, - className: containerClassName, - children, + narrow = false, ...rest }) { const prefix = usePrefix(); const { subgrid } = useGridSettings(); - const className = cx(containerClassName, { - [`${prefix}--css-grid`]: !subgrid, - [`${prefix}--css-grid--${columns}`]: !subgrid && columns !== 16, - [`${prefix}--css-grid--condensed`]: condensed, + let mode = 'wide'; + if (narrow) { + mode = 'narrow'; + } else if (condensed) { + mode = 'condensed'; + } + + if (subgrid) { + return ( + + + {children} + + + ); + } + + const className = cx(customClassName, { + [`${prefix}--css-grid`]: true, + [`${prefix}--css-grid--condensed`]: mode === 'condensed', + [`${prefix}--css-grid--narrow`]: mode === 'narrow', [`${prefix}--css-grid--full-width`]: fullWidth, - [`${prefix}--subgrid`]: subgrid, - [`${prefix}--col-span-${columns}`]: - (subgrid && columns !== 16) || columns !== 16, }); return ( @@ -57,11 +75,6 @@ CSSGrid.propTypes = { */ className: PropTypes.string, - /** - * Specify how many columns wide the Grid should span - */ - columns: PropTypes.number, - /** * Collapse the gutter to 1px. Useful for fluid layouts. * Rows have 1px of margin between them to match gutter. @@ -72,6 +85,55 @@ CSSGrid.propTypes = { * Remove the default max width that the grid has set */ fullWidth: PropTypes.bool, + + /** + * Container hangs 16px into the gutter. Useful for + * typographic alignment with and without containers. + */ + narrow: PropTypes.bool, +}; + +function Subgrid({ + as: BaseComponent = 'div', + className: customClassName, + children, + mode, + ...rest +}) { + const prefix = usePrefix(); + const className = cx(customClassName, { + [`${prefix}--subgrid`]: true, + [`${prefix}--subgrid--condensed`]: mode === 'condensed', + [`${prefix}--subgrid--narrow`]: mode === 'narrow', + [`${prefix}--subgrid--wide`]: mode === 'wide', + }); + return ( + + {children} + + ); +} + +Subgrid.propTypes = { + /** + * Provide a custom element to render instead of the default
+ */ + as: PropTypes.oneOfType([PropTypes.string, PropTypes.elementType]), + + /** + * Pass in content that will be rendered within the `Subgrid` + */ + children: PropTypes.node, + + /** + * Specify a custom className to be applied to the `Subgrid` + */ + className: PropTypes.string, + + /** + * Specify the grid mode for the subgrid + */ + mode: PropTypes.oneOf(['wide', 'narrow', 'condensed']), }; export { CSSGrid }; diff --git a/packages/react/src/components/Grid/Column.js b/packages/react/src/components/Grid/Column.js index d29045d2c23b..1b810d6dddb4 100644 --- a/packages/react/src/components/Grid/Column.js +++ b/packages/react/src/components/Grid/Column.js @@ -15,7 +15,7 @@ import { useGridSettings } from './GridContext'; function Column({ as: BaseComponent = 'div', children, - className: containerClassName, + className: customClassName, sm, md, lg, @@ -25,12 +25,28 @@ function Column({ }) { const { mode } = useGridSettings(); const prefix = usePrefix(); - const columnClassName = - mode === 'css-grid' - ? getClassNameForBreakpoints([sm, md, lg, xlg, max], prefix) - : getClassNameForFlexGridBreakpoints([sm, md, lg, xlg, max], prefix); - const className = cx(containerClassName, columnClassName, { + if (mode === 'css-grid') { + return ( + + {children} + + ); + } + + const columnClassName = getClassNameForFlexGridBreakpoints( + [sm, md, lg, xlg, max], + prefix + ); + const className = cx(customClassName, columnClassName, { [`${prefix}--col`]: columnClassName.length === 0, }); @@ -121,6 +137,106 @@ Column.propTypes = { xlg: spanPropType, }; +function CSSGridColumn({ + as: BaseComponent = 'div', + children, + className: containerClassName, + sm, + md, + lg, + xlg, + max, + span, + ...rest +}) { + const prefix = usePrefix(); + const breakpointClassName = getClassNameForBreakpoints( + [sm, md, lg, xlg, max], + prefix + ); + const spanClassName = getClassNameForSpan(span, prefix); + const className = cx(containerClassName, breakpointClassName, spanClassName, { + [`${prefix}--css-grid-column`]: true, + }); + + return ( + + {children} + + ); +} + +CSSGridColumn.propTypes = { + /** + * Provide a custom element to render instead of the default
+ */ + as: PropTypes.oneOfType([PropTypes.string, PropTypes.elementType]), + + /** + * Pass in content that will be rendered within the `Column` + */ + children: PropTypes.node, + + /** + * Specify a custom className to be applied to the `Column` + */ + className: PropTypes.string, + + /** + * Specify column span for the `lg` breakpoint (Default breakpoint up to 1312px) + * This breakpoint supports 16 columns by default. + * + * @see https://www.carbondesignsystem.com/guidelines/layout#breakpoints + */ + lg: spanPropType, + + /** + * Specify column span for the `max` breakpoint. This breakpoint supports 16 + * columns by default. + * + * @see https://www.carbondesignsystem.com/guidelines/layout#breakpoints + */ + max: spanPropType, + + /** + * Specify column span for the `md` breakpoint (Default breakpoint up to 1056px) + * This breakpoint supports 8 columns by default. + * + * @see https://www.carbondesignsystem.com/guidelines/layout#breakpoints + */ + md: spanPropType, + + /** + * Specify column span for the `sm` breakpoint (Default breakpoint up to 672px) + * This breakpoint supports 4 columns by default. + * + * @see https://www.carbondesignsystem.com/guidelines/layout#breakpoints + */ + sm: spanPropType, + + /** + * Specify constant column span, start, or end values that will not change + * based on breakpoint + */ + span: PropTypes.oneOfType([ + PropTypes.number, + percentSpanType, + PropTypes.shape({ + span: PropTypes.oneOfType([PropTypes.number, percentSpanType]), + start: PropTypes.number, + end: PropTypes.number, + }), + ]), + + /** + * Specify column span for the `xlg` breakpoint (Default breakpoint up to + * 1584px) This breakpoint supports 16 columns by default. + * + * @see https://www.carbondesignsystem.com/guidelines/layout#breakpoints + */ + xlg: spanPropType, +}; + const breakpointNames = ['sm', 'md', 'lg', 'xlg', 'max']; /** @@ -238,4 +354,31 @@ function getClassNameForFlexGridBreakpoints(breakpoints, prefix) { return classNames.join(' '); } +/** + * Build the appropriate className for a span value + */ +function getClassNameForSpan(value, prefix) { + const classNames = []; + + if (typeof value === 'number' || typeof value === 'string') { + classNames.push(`${prefix}--col-span-${value}`); + } else if (typeof value === 'object') { + const { span, start, end } = value; + + if (span !== undefined && span !== null) { + classNames.push(`${prefix}--col-span-${span}`); + } + + if (start !== undefined && start !== null) { + classNames.push(`${prefix}--col-start-${start}`); + } + + if (end !== undefined && end !== null) { + classNames.push(`${prefix}--col-end-${end}`); + } + } + + return classNames.join(''); +} + export default Column; diff --git a/packages/react/src/components/Grid/ColumnHang.js b/packages/react/src/components/Grid/ColumnHang.js new file mode 100644 index 000000000000..e5de6cca3ae3 --- /dev/null +++ b/packages/react/src/components/Grid/ColumnHang.js @@ -0,0 +1,49 @@ +/** + * Copyright IBM Corp. 2016, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import cx from 'classnames'; +import PropTypes from 'prop-types'; +import React from 'react'; +import { usePrefix } from '../../internal/usePrefix'; + +/** + * Helper component for rendering content that hangs on the column. Useful when + * trying to align content across different grid modes + */ +function ColumnHang({ + as: BaseComponent = 'div', + className: customClassName, + children, + ...rest +}) { + const prefix = usePrefix(); + const className = cx(customClassName, `${prefix}--grid-column-hang`); + return ( + + {children} + + ); +} + +ColumnHang.propTypes = { + /** + * Provide a custom element to render instead of the default
+ */ + as: PropTypes.oneOfType([PropTypes.string, PropTypes.elementType]), + + /** + * Pass in content that will be rendered within the `Grid` + */ + children: PropTypes.node, + + /** + * Specify a custom className to be applied to the `Grid` + */ + className: PropTypes.string, +}; + +export { ColumnHang }; diff --git a/packages/react/src/components/Grid/__tests__/Grid-test.e2e.js b/packages/react/src/components/Grid/__tests__/Grid-test.e2e.js index 21f852b70d65..88eca25a6db7 100644 --- a/packages/react/src/components/Grid/__tests__/Grid-test.e2e.js +++ b/packages/react/src/components/Grid/__tests__/Grid-test.e2e.js @@ -5,7 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -import 'carbon-components/scss/globals/scss/vendor/@carbon/grid/scss/modules/_css-grid.scss'; import './Grid-test.e2e.scss'; import React from 'react'; diff --git a/packages/react/src/components/Grid/__tests__/Grid-test.e2e.scss b/packages/react/src/components/Grid/__tests__/Grid-test.e2e.scss index 5e2ddad08093..36d7a9615af6 100644 --- a/packages/react/src/components/Grid/__tests__/Grid-test.e2e.scss +++ b/packages/react/src/components/Grid/__tests__/Grid-test.e2e.scss @@ -1,3 +1,8 @@ +@use '~@carbon/styles/scss/config' with ( + $prefix: 'bx', +); +@use '~@carbon/styles/scss/grid'; + @import '~carbon-components/scss/globals/scss/vars'; @import '~carbon-components/scss/globals/scss/colors'; @import '~carbon-components/scss/globals/scss/typography'; diff --git a/packages/react/src/components/Grid/index.js b/packages/react/src/components/Grid/index.js index 6ba488c90806..323e5b2c0840 100644 --- a/packages/react/src/components/Grid/index.js +++ b/packages/react/src/components/Grid/index.js @@ -9,3 +9,4 @@ export { FlexGrid } from './FlexGrid'; export { Grid } from './Grid'; export { default as Row } from './Row'; export { default as Column } from './Column'; +export { ColumnHang } from './ColumnHang'; diff --git a/packages/react/src/components/Grid/next/Grid.stories.js b/packages/react/src/components/Grid/next/Grid.stories.js index bffce7f2b69f..ae2afc29d7a3 100644 --- a/packages/react/src/components/Grid/next/Grid.stories.js +++ b/packages/react/src/components/Grid/next/Grid.stories.js @@ -8,7 +8,7 @@ import './Grid.stories.scss'; import React from 'react'; -import { Grid, Column } from '../../Grid'; +import { Grid, Column, ColumnHang } from '../../Grid'; import mdx from './Grid.mdx'; export default { @@ -19,7 +19,6 @@ export default { }, parameters: { controls: { - include: [], // ensure props are not displayed on the controls pane hideNoControlsWarning: true, }, docs: { @@ -37,7 +36,7 @@ export default { ], }; -export const Wide = () => { +export const Default = () => { return ( @@ -48,6 +47,17 @@ export const Wide = () => { ); }; +export const Narrow = () => { + return ( + + + + + + + ); +}; + export const Condensed = () => { return ( @@ -148,36 +158,69 @@ export const Subgrid = () => { export const MixedGridModes = () => { return ( - - - - -

wide

-
-
-
- - - -

condensed

-
-
-
- - - -

condensed

-
-
-
- - - -

wide

-
-
-
-
+ <> + + + + + + + Text + + + Text + + + Text + + + Text + + + + Text + Text + + + + Text + + + Text + + + + + + + + + + + + + + + + + + Text + + + Text + + + Text + + + Text + + + + + + + ); }; @@ -229,18 +272,3 @@ export const Offset = () => ( />
); - -export const AutoColumns = () => { - return ( - - - - - - - - - - - ); -}; diff --git a/packages/react/src/components/Grid/next/Grid.stories.scss b/packages/react/src/components/Grid/next/Grid.stories.scss index 84ab69d043f0..c0b0387c6f68 100644 --- a/packages/react/src/components/Grid/next/Grid.stories.scss +++ b/packages/react/src/components/Grid/next/Grid.stories.scss @@ -1,46 +1,101 @@ +// +// Copyright IBM Corp. 2018, 2018 +// +// This source code is licensed under the Apache-2.0 license found in the +// LICENSE file in the root directory of this source tree. +// + @use '@carbon/styles/scss/colors' as *; @use '@carbon/styles/scss/type' as *; -// Grid modes -.sb-css-grid-container .cds--css-grid { - background-color: $blue-20; - outline: 1px dashed $blue-40; -} +.sb-css-grid-container { + // Grid modes + .cds--css-grid { + background-color: $blue-20; + outline: 1px dashed $blue-40; + } -.sb-css-grid-container .cds--css-grid p { - @include type-style('code-02'); -} + .cds--css-grid p { + @include type-style('code-02'); + } -.sb-css-grid-container .cds--css-grid p:first-of-type { - //messing up progress indicator stories - margin-top: 0; -} + .cds--css-grid p:first-of-type { + margin-top: 0; + } -.sb-css-grid-container .cds--css-grid.cds--css-grid--condensed { - background-color: $purple-20; - outline: 1px dashed $purple-40; -} + // Narrow + .cds--css-grid.cds--css-grid--narrow { + background-color: #d6f9f9; + outline: 1px dashed $green-40; + } -// Only use background for subgrid example, not other subgrids in "mixed modes" story or others -.sb-css-grid-container .cds--subgrid.example { - background-color: $green-20; -} + // Condensed + .cds--css-grid.cds--css-grid--condensed { + background-color: $purple-10; + outline: 1px dashed $purple-40; + } -// Columns -.sb-css-grid-container .cds--css-grid > [class*='col'], -.sb-css-grid-container .cds--subgrid > [class*='col'] { - min-height: 80px; -} + .cds--subgrid { + outline: 1px solid black; + padding-top: 2rem; + padding-bottom: 2rem; + position: relative; + background: #eef4ff; + } -.sb-css-grid-container .cds--css-grid > [class*='col'] { - background: $blue-10; -} + .cds--css-grid, + .cds--subgrid--wide { + --grid-mode-color: #97c1ff; + } -.sb-css-grid-container .cds--subgrid > [class*='col'] { - background: $green-10; - outline: 1px dashed $green-30; -} + .cds--css-grid--narrow, + .cds--subgrid--narrow { + --grid-mode-color: #20d5d2; + background: $green-10; + } + + .cds--css-grid--condensed, + .cds--subgrid--condensed { + --grid-mode-color: #bb8eff; + background: $purple-10; + } + + .cds--subgrid--narrow { + background: #d6f9f9; + } + + .cds--subgrid--condensed { + background: #f7f2ff; + } + + .cds--subgrid::before { + @include type-style('code-01'); + position: absolute; + inset-block-start: 0; + inset-inline-start: 0; + display: block; + content: 'subgrid'; + background: var(--grid-mode-color, #97c1ff); + color: $black; + padding: 0.125rem 0.25rem; + } + + // Column + .cds--css-grid-column { + --border-color: #97c1ff; + + background: $white; + box-shadow: 0 0 0 1px var(--border-color); + min-height: 80px; + } + + .cds--css-grid--narrow .cds--css-grid-column, + .cds--subgrid--narrow .cds--css-grid-column { + --border-color: #20d5d2; + } -.sb-css-grid-container.cds--css-grid.cds--css-grid--condensed > [class*='col'] { - background: $purple-10; + .cds--css-grid--condensed .cds--css-grid-column, + .cds--subgrid--condensed .cds--css-grid-column { + --border-color: #bb8eff; + } } diff --git a/packages/react/src/index.js b/packages/react/src/index.js index ceab213d426c..008f303612e6 100644 --- a/packages/react/src/index.js +++ b/packages/react/src/index.js @@ -68,6 +68,7 @@ export { Grid, Row, Column, + ColumnHang as unstable_ColumnHang, FlexGrid as unstable_FlexGrid, } from './components/Grid'; export Icon from './components/Icon'; diff --git a/packages/styles/docs/sass.md b/packages/styles/docs/sass.md index dcb6181b0f4d..2a2a83fe6cc8 100644 --- a/packages/styles/docs/sass.md +++ b/packages/styles/docs/sass.md @@ -122,28 +122,45 @@ to `false`: ## Grid -| Import | Filepath | -| :--------------------------------- | :---------------- | -| `@use '@carbon/styles/scss/grid';` | `scss/_grid.scss` | +| Import | Filepath | +| :----------------------------------------- | :------------------------ | +| `@use '@carbon/styles/scss/grid';` | `scss/grid/_index.scss` | +| `@use '@carbon/styles/scss/grid/flexbox';` | `scss/grid/_flexbox.scss` | ### Using the grid This package `@forward`s the styles defined in the [`@carbon/grid`](https://github.com/carbon-design-system/carbon/tree/main/packages/grid) -package. The full source for Carbon grid styles can be found there alongside -more comprehensive documentation on package contents, configuration, and usage. +package. For full documentation, visit the +[Sass Documentation](../../grid/docs/sass.md) for the package. To use the grid via `@carbon/styles`, it must be brought in directly or the grid specific style sheet must be imported: ```scss -/* All the grid styles are included through this central entrypoint */ +// All the grid styles are included through this central entrypoint @use '@carbon/styles'; -/* Alternatively, the grid styles can be brought in on their own */ +// Alternatively, the grid styles can be brought in on their own @use '@carbon/styles/scss/grid'; ``` +By default, the emitted grid will be a CSS Grid based implementation. If you +prefer to use flexbox version, you can configure the package by writing the +following: + +```scss +@use '@carbon/styles' with ( + $use-flexbox-grid: true, +); +``` + +Or you can import the flexbox grid directly: + +```scss +@use '@carbon/styles/scss/grid/flexbox'; +``` + ### Classes provided In either case, you will then have access to the grid classes and mixins @@ -172,22 +189,6 @@ prefix:
``` -### Grid Mixins - -In the event that you'd like to configure your own classes for portions of the -grid, there are a few mixins that can be used. - -- `css-grid()` provides the base grid definition -- `subgrid()` provides the subgrid definition -- `carbon--aspect-ratio($aspect-ratios: $carbon--aspect-ratios)` generates the - CSS classname utilities for the aspect ratios - -```scss -.custom-grid-class { - @include css-grid(); -} -``` - ## Motion The motion package provides helper functions, mixins, and duration tokens to add diff --git a/packages/styles/scss/__tests__/__snapshots__/config-test.js.snap b/packages/styles/scss/__tests__/__snapshots__/config-test.js.snap index 303394cf3537..aa4d35140b09 100644 --- a/packages/styles/scss/__tests__/__snapshots__/config-test.js.snap +++ b/packages/styles/scss/__tests__/__snapshots__/config-test.js.snap @@ -9,6 +9,7 @@ Object { "font-display": "swap", "font-path": "~@ibm/plex", "prefix": "cds", + "use-flexbox-grid": false, "use-google-fonts": false, } `; diff --git a/packages/styles/scss/__tests__/grid-test.js b/packages/styles/scss/__tests__/grid-test.js index ce40d768483e..e2522e4e49e5 100644 --- a/packages/styles/scss/__tests__/grid-test.js +++ b/packages/styles/scss/__tests__/grid-test.js @@ -22,11 +22,11 @@ describe('@carbon/styles/scss/grid', () => { $_: get('api', ( variables: ( grid-gutter: meta.variable-exists('grid-gutter'), + grid-gutter-condensed: meta.variable-exists('grid-gutter-condensed'), grid-breakpoints: meta.variable-exists('grid-breakpoints'), ), mixins: ( css-grid: meta.mixin-exists('css-grid'), - subgrid: meta.mixin-exists('subgrid'), flex-grid: meta.mixin-exists('flex-grid'), ), )); @@ -36,11 +36,11 @@ describe('@carbon/styles/scss/grid', () => { expect(api).toEqual({ variables: { 'grid-gutter': true, + 'grid-gutter-condensed': true, 'grid-breakpoints': true, }, mixins: { 'css-grid': true, - subgrid: true, 'flex-grid': true, }, }); diff --git a/packages/styles/scss/_config.scss b/packages/styles/scss/_config.scss index 6f999508f5ea..b62ca62f3958 100644 --- a/packages/styles/scss/_config.scss +++ b/packages/styles/scss/_config.scss @@ -54,3 +54,8 @@ $prefix: 'cds' !default; /// @access public /// @group @carbon/grid $flex-grid-columns: 16 !default; + +/// Specify if the default grid type should be the flexbox grid +/// @type Boolean +/// @group config +$use-flexbox-grid: false !default; diff --git a/packages/styles/scss/_grid.scss b/packages/styles/scss/grid/_config.scss similarity index 70% rename from packages/styles/scss/_grid.scss rename to packages/styles/scss/grid/_config.scss index 7149ecf3ee12..a54efc0ae21e 100644 --- a/packages/styles/scss/_grid.scss +++ b/packages/styles/scss/grid/_config.scss @@ -5,18 +5,14 @@ // LICENSE file in the root directory of this source tree. // -@use 'config'; - +@use '../config'; @forward '@carbon/grid' show css-grid, - subgrid, flex-grid, $grid-gutter, $grid-gutter-condensed, $grid-breakpoints with ( - $prefix: config.$prefix, - $flex-grid-columns: config.$flex-grid-columns, + $prefix: config.$prefix !default, + $flex-grid-columns: config.$flex-grid-columns !default ); - -@use '@carbon/grid'; diff --git a/packages/grid/examples/css-grid/src/scss/components/_index.scss b/packages/styles/scss/grid/_flexbox.scss similarity index 72% rename from packages/grid/examples/css-grid/src/scss/components/_index.scss rename to packages/styles/scss/grid/_flexbox.scss index af0a3e727674..22973473ed67 100644 --- a/packages/grid/examples/css-grid/src/scss/components/_index.scss +++ b/packages/styles/scss/grid/_flexbox.scss @@ -5,4 +5,7 @@ // LICENSE file in the root directory of this source tree. // -@use 'header'; +@forward 'config'; +@use '@carbon/grid'; + +@include grid.flex-grid(); diff --git a/packages/styles/scss/grid/_index.scss b/packages/styles/scss/grid/_index.scss new file mode 100644 index 000000000000..d3bab7bca1bc --- /dev/null +++ b/packages/styles/scss/grid/_index.scss @@ -0,0 +1,16 @@ +// +// Copyright IBM Corp. 2018, 2018 +// +// This source code is licensed under the Apache-2.0 license found in the +// LICENSE file in the root directory of this source tree. +// + +@forward 'config'; +@use '@carbon/grid'; +@use '../config'; + +@if config.$use-flexbox-grid == true { + @include grid.flex-grid(); +} @else { + @include grid.css-grid(); +} diff --git a/packages/type/scss/modules/_styles.scss b/packages/type/scss/modules/_styles.scss index b3ca5c41b248..00da20725be6 100644 --- a/packages/type/scss/modules/_styles.scss +++ b/packages/type/scss/modules/_styles.scss @@ -9,7 +9,7 @@ @use 'sass:map'; @use 'sass:math'; -@use '@carbon/grid/scss/modules/breakpoint' as grid; +@use '@carbon/grid/scss/modules/config' as grid; @use 'font-family'; @use 'scale';