├── .editorconfig ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .prettierignore ├── README.md ├── example ├── .storybook │ ├── main.ts │ └── preview.ts ├── Example.stories.tsx ├── package.json └── tsconfig.json ├── index.d.ts ├── license ├── package.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── shots ├── example.png ├── logo.svg └── square_logo.png ├── src ├── Grids.tsx ├── Tool.tsx ├── chromatic.ts ├── constants.ts ├── index.ts ├── manager.tsx ├── preview.ts └── ui.tsx └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = tab 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: {} 7 | 8 | jobs: 9 | test: 10 | name: Node.js v${{ matrix.node }} 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | node: [20, 22, 23] 15 | steps: 16 | - uses: actions/checkout@main 17 | 18 | - name: (env) setup pnpm 19 | uses: pnpm/action-setup@v4.1.0 20 | with: 21 | version: 10.10.0 22 | 23 | - name: (env) setup node v${{ matrix.node }} 24 | uses: actions/setup-node@main 25 | with: 26 | node-version: ${{ matrix.node }} 27 | cache: pnpm 28 | 29 | - name: (env) globals 30 | run: pnpm add -g oxlint 31 | 32 | - run: pnpm install 33 | - run: oxlint . 34 | - run: pnpm run build 35 | - run: pnpm run typecheck 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | *-lock.json 4 | *.lock 5 | *.log 6 | 7 | /coverage 8 | /.nyc_output 9 | 10 | # Editors 11 | *.iml 12 | /.idea 13 | /.vscode 14 | 15 | # Code 16 | /dist 17 | /*.mjs 18 | /*.js -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .gitignore -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |

storybook-addon-grid

6 | 7 |
8 | 9 | `npm install storybook-addon-grid` keeps your stories in rhythm 10 | 11 | 12 | js downloads 13 | 14 | 15 | licenses 16 | 17 | 18 |
19 |
20 | 21 | 22 | 23 | This is free to use software, but if you do like it, consider supporting me ❤️ 24 | 25 | [![sponsor me](https://badgen.net/badge/icon/sponsor?icon=github&label&color=gray)](https://github.com/sponsors/maraisr) 26 | [![buy me a coffee](https://badgen.net/badge/icon/buymeacoffee?icon=buymeacoffee&label&color=gray)](https://www.buymeacoffee.com/marais) 27 | 28 | 29 | 30 |
31 | 32 | ![example that shows how the columns look when enabled](./shots/example.png) 33 | 34 | ## ⚙️ Install 35 | 36 | ```sh 37 | npm install storybook-addon-grid 38 | ``` 39 | 40 | ```js 41 | // .storybook/main.js 42 | const config = { 43 | addons: ['storybook-addon-grid'], 44 | }; 45 | ``` 46 | 47 | >
48 | > Chromatic users 49 | > 50 | > Include this additional preset to configure the column guides for your Chromatic screenshots. 51 | > 52 | > ```js 53 | > // .storybook/main.js 54 | > const config = { 55 | > addons: ['storybook-addon-grid', 'storybook-addon-grid/chromatic'], 56 | > }; 57 | > ``` 58 | > 59 | >
60 | 61 | ## 🚀 Usage 62 | 63 | The column guides are controlled with [parameters](https://storybook.js.org/docs/react/writing-stories/parameters) and 64 | as such you can define this globally or per story. 65 | 66 | The column guides can be turned on either via clicking the toolbar button, or via a keyboard shortcut Ctrl + 67 | G. 68 | 69 | > **Note:** Due to the nature of `z-index`, the root `div` of the stories will have a `position: relative` and 70 | > `z-index: 0` applied to it, allowing the column guides to sit over the top. 71 | 72 | ### _Parameters_ 73 | 74 | Column design system is defined by 3 values: 75 | 76 | - the number of `columns` 77 | - the `gap` between them 78 | - the `gutter` — minimal margin between the system and the screen 79 | - `maximal-width` for the system to limit maximum width of all columns as well. 80 | 81 | #### `columns?: number = 12` 82 | 83 | The number of columns guides. 84 | 85 | #### `gap?: string = '20px'` 86 | 87 | The gap between `columns`. 88 | 89 | #### `gutter?: string = '50px'` 90 | 91 | System's gutter (`margin`) for both left and right. 92 | 93 | Define to override the gutter defined on the right-hand-side. 94 | 95 | #### `maxWidth?: string = '1024px'` 96 | 97 | The maximum width our columns should grow. 98 | 99 | #### `color?: string = 'rgba(255, 0, 0, 0.1)'` 100 | 101 | Sets the color used for the column guides. 102 | 103 | ##### _Global Parameters~_ 104 | 105 | ```js 106 | // .storybook/preview.js 107 | export const parameters = { 108 | grid: { 109 | gridOn: true, 110 | columns: 12, 111 | gap: '20px', 112 | gutter: '50px', 113 | maxWidth: '1024px', 114 | }, 115 | }; 116 | ``` 117 | 118 | ##### _Per story~_ 119 | 120 | ```js 121 | // MyComponent.stories.js 122 | export const Example = () => {...}; 123 | Example.parameters = { 124 | grid: { 125 | columns: 6, 126 | }, 127 | }; 128 | ``` 129 | 130 | ### Responsive properties 131 | 132 | The way `storybook-addon-grid` solves responsive properties is leaving this up to you. We don't you to build 133 | abstractions and implementations for this addon, we want to reuse existing patterns you may already be using. 134 | 135 | In fact all properties map through to css, so any css variable you expose is consumable. 136 | 137 | eg: 138 | 139 | ```css 140 | // file: my-styles.css 141 | @media (min-width: 768px) { 142 | :root { 143 | --columns: 8; 144 | --gap: 12px; 145 | --gutter: 24px; 146 | } 147 | } 148 | ``` 149 | 150 | ```ts 151 | Story.parameters = { 152 | grid: { 153 | // a custom variable names for the number of columns 154 | columns: 'var(--columns)', 155 | // or the gutter 156 | gutter: 'var(--gutter)', 157 | // or the gap 158 | gap: 'var(--gap)', 159 | }, 160 | }; 161 | ``` 162 | 163 | You can see this in action over at our [example story `ResponsiveGrid`](./example/Example.stories.tsx). 164 | 165 | ## 📚 Further Readings 166 | 167 | - https://compassofdesign.com/articles/design-principle-1-guides-gutters-and-grids 168 | 169 | ## ❤ Thanks 170 | 171 | Special thanks to [Marina](https://github.com/thearnica) for the initial implementation and design. 172 | 173 | ## License 174 | 175 | MIT © [Marais Rossouw](https://marais.io) 176 | -------------------------------------------------------------------------------- /example/.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from '@storybook/react-vite'; 2 | 3 | const config: StorybookConfig = { 4 | stories: ['../*.stories.*'], 5 | addons: ['storybook-addon-grid'], 6 | framework: { 7 | name: '@storybook/react-vite', 8 | options: {}, 9 | }, 10 | }; 11 | export default config; 12 | -------------------------------------------------------------------------------- /example/.storybook/preview.ts: -------------------------------------------------------------------------------- 1 | import type { Preview } from '@storybook/react'; 2 | 3 | const preview: Preview = { 4 | parameters: { 5 | grid: { 6 | gap: '30px', 7 | }, 8 | }, 9 | }; 10 | 11 | export default preview; 12 | -------------------------------------------------------------------------------- /example/Example.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | 3 | const ComponentTest = () => ( 4 |
11 | Rendered at {new Date().toISOString()} 12 |
13 | ); 14 | 15 | export default { 16 | title: 'Example', 17 | component: ComponentTest, 18 | } as Meta; 19 | 20 | export const Default = {} as StoryObj; 21 | 22 | export const Disabled = { 23 | parameters: { 24 | grid: { 25 | disable: true, 26 | }, 27 | }, 28 | } as StoryObj; 29 | 30 | export const SixColumnGrid = { 31 | parameters: { 32 | grid: { 33 | columns: 6, 34 | }, 35 | }, 36 | } as StoryObj; 37 | 38 | export const GapColumnsChanged = { 39 | parameters: { 40 | grid: { 41 | columns: 4, 42 | gap: '10px', 43 | }, 44 | }, 45 | } as StoryObj; 46 | 47 | export const ZIndexCheckAbsolute = { 48 | render() { 49 | return ( 50 |
51 | 52 |
53 | ); 54 | }, 55 | } as StoryObj; 56 | 57 | export const ZIndexCheckFixed = { 58 | render() { 59 | return ( 60 |
61 | 62 |
63 | ); 64 | }, 65 | } as StoryObj; 66 | export const DifferentGutters = { 67 | parameters: { 68 | grid: { 69 | gutter: ['240px'], 70 | }, 71 | }, 72 | } as StoryObj; 73 | 74 | export const DifferentColor = { 75 | parameters: { 76 | grid: { 77 | color: 'rgba(0, 0, 255, 0.1)', 78 | }, 79 | }, 80 | } as StoryObj; 81 | 82 | export const ResponsiveGrid = { 83 | render() { 84 | return ( 85 | <> 86 |