├── .nvmrc ├── examples ├── .nvmrc ├── .eslintrc ├── next.config.js ├── examples.png ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── pages │ ├── _app.js │ └── index.js ├── styles │ └── globals.css ├── .gitignore ├── package.json ├── tailwind.config.js └── README.md ├── .gitignore ├── .prettierrc ├── package.json ├── README.md └── src └── tailwind-container-break-out.js /.nvmrc: -------------------------------------------------------------------------------- 1 | lts/hydrogen -------------------------------------------------------------------------------- /examples/.nvmrc: -------------------------------------------------------------------------------- 1 | lts/jod -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /examples/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next", "next/core-web-vitals"] 3 | } 4 | -------------------------------------------------------------------------------- /examples/next.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | reactStrictMode: true, 3 | } 4 | -------------------------------------------------------------------------------- /examples/examples.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucidNinja/tailwind-container-break-out/HEAD/examples/examples.png -------------------------------------------------------------------------------- /examples/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucidNinja/tailwind-container-break-out/HEAD/examples/public/favicon.ico -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "tabWidth": 2, 4 | "printWidth": 120, 5 | "trailingComma": "none", 6 | "arrowParens": "avoid" 7 | } 8 | -------------------------------------------------------------------------------- /examples/pages/_app.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import setScrollbarWidth from 'set-scrollbar-width'; 3 | import '../styles/globals.css'; 4 | 5 | function MyApp({ Component, pageProps }) { 6 | useEffect(() => { 7 | setScrollbarWidth(); 8 | }, []); 9 | 10 | return ; 11 | } 12 | 13 | export default MyApp; 14 | -------------------------------------------------------------------------------- /examples/styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | .bg-stripes { 6 | background-image: linear-gradient( 7 | 45deg, 8 | #10A5E9 12.5%, 9 | transparent 12.5%, 10 | transparent 50%, 11 | #10A5E9 50%, 12 | #10A5E9 62.5%, 13 | transparent 62.5%, 14 | transparent 100% 15 | ); 16 | background-size: 5.66px 5.66px; 17 | } 18 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | -------------------------------------------------------------------------------- /examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "examples", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "next": "^14.1.3", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0", 15 | "set-scrollbar-width": "^1.0.4", 16 | "tailwind-container-break-out": "file:.." 17 | }, 18 | "devDependencies": { 19 | "autoprefixer": "^10.4.18", 20 | "eslint": "7.29.0", 21 | "eslint-config-next": "^14.1.3", 22 | "postcss": "^8.4.35", 23 | "tailwindcss": "^3.4.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], 3 | theme: { 4 | container: { 5 | center: true, 6 | padding: { 7 | DEFAULT: '1rem', 8 | sm: '2rem', 9 | lg: '3rem', 10 | xl: '4rem' 11 | } 12 | }, 13 | extend: { 14 | colors: { 15 | blue: { DEFAULT: '#10A5E9', light: '#E0F1FE', lightest: '#F0F8FF' } 16 | } 17 | }, 18 | screens: { 19 | sm: '640px', 20 | md: '768px', 21 | lg: '1024px', 22 | xl: '1280px', 23 | '2xl': '1536px' 24 | } 25 | }, 26 | variants: { 27 | extend: {} 28 | }, 29 | plugins: [require('tailwind-container-break-out')] 30 | }; 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tailwind-container-break-out", 3 | "version": "2.0.9", 4 | "description": "A TailwindCSS plugin for breaking out of containers.", 5 | "main": "src/tailwind-container-break-out.js", 6 | "files": [ 7 | "src/**/*" 8 | ], 9 | "scripts": { 10 | "test": "echo \"No test specified\"" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/LucidNinja/tailwind-container-break-out.git" 15 | }, 16 | "keywords": [ 17 | "tailwindcss", 18 | "container", 19 | "margins", 20 | "pull" 21 | ], 22 | "author": "Wake Up, Dreamer", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/LucidNinja/tailwind-container-break-out/issues" 26 | }, 27 | "homepage": "https://github.com/LucidNinja/tailwind-container-break-out#readme", 28 | "peerDependencies": { 29 | "tailwindcss": ">=2.0.0" 30 | }, 31 | "dependencies": {} 32 | } 33 | -------------------------------------------------------------------------------- /examples/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | 15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. 16 | 17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. 18 | 19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tailwind Container Break Out 2 | 3 | This is a TailwindCSS plugin that is used to 'break' child elements out of the Tailwind `.container` class. It works with any container padding customisations or custom breakpoints that are set within your `tailwind.config.js`. 4 | 5 | For best results, set your `.container` to center mode in your `tailwind.config.js`, or use `mx-auto` whenever you use the `.container` class. 6 | 7 | The `m{l|r|x}-break-out` and `p{l|r|x}-break-out` classes can be used anywhere inside of a container, but for the most reliable results, apply them to a direct child of `.container`. 8 | 9 | These classes are simple utilities that calculate the negative margins (and inner padding, where required) needed for an element to 'break out' of the container at each responsive break point - they do not account for any extra padding or margins that have been added to your markup. 10 | 11 | ## Installation 12 | 13 | `npm i tailwind-container-break-out` 14 | 15 | For best browser compatibility that combats varying scrollbar behaviour, also install [Set Scrollbar Width](https://github.com/LucidNinja/set-scrollbar-width) and run `setScrollbarWidth()` on app mount 16 | 17 | `npm i set-scrollbar-width` 18 | 19 | ## Quick Start 20 | 21 | ### Require the plugin in your tailwind.config.js 22 | 23 | ``` 24 | // tailwind.config.js 25 | 26 | module.exports = { 27 | theme: { 28 | container: { 29 | center: true, 30 | padding: '1rem' 31 | }, 32 | }, 33 | plugins: [require('tailwind-container-break-out')] 34 | }; 35 | 36 | ``` 37 | 38 | ### Use the `m{l|r|x}-break-out` and `p{l|r|x}-break-out` classes in your markup. 39 | 40 | - Full width break out 41 | 42 | ``` 43 |
44 |

This content is in a container

45 |
46 | 47 |

This whole div will expand beyond the container on both the left and right.

48 |
49 |
50 | ``` 51 | 52 | - Break out on one side only 53 | 54 | ``` 55 |
56 |
57 |

This content is in a container, and sits on the left side of the grid.

58 |
59 |
60 | 61 |

This div will expand beyond the container on the right hand side.

62 |
63 |
64 | ``` 65 | 66 | - Break out on one side only, with `p{l|r|x}-break-out` classes to keep inner content in line with `.container` 67 | 68 | ``` 69 |
70 |
71 |

This content is in a container, and sits on the left side of the grid.

72 |

The background color will break out of the `.container` but this text will align to the `.container` edge. 73 |

74 |
76 |

This div will expand beyond the container on the right hand side.

77 |
78 |
79 | ``` 80 | 81 | 82 | ## Examples 83 | 84 | ![Tailwind Container Break Out Examples](./examples/examples.png?raw=true 'Tailwind Container Break Out Examples') 85 | 86 | ## Working example with NextJS 87 | 88 | See working example [here](https://tailwind-container-break-out-beryl.vercel.app/). 89 | 90 | --- 91 | 92 | ## ⚠️ A note on browser compatibility 93 | This package may produce unintented scrollbar behaviour in some browsers, and so it is encouraged to use alongside this package https://github.com/LucidNinja/set-scrollbar-width, in order to set the scrollbar width as the `--twcb-scrollbar-width` CSS variable. By default, this is set as 0px, but should be overriden using [this package](https://github.com/LucidNinja/set-scrollbar-width) or Javascript in the framework of your choosing. -------------------------------------------------------------------------------- /examples/pages/index.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head'; 2 | 3 | export default function Home() { 4 | return ( 5 |
6 | 7 | Tailwind Container Break Out Examples 8 | 9 | 10 | 11 | 12 |
13 |

Tailwind Container Break Out Examples

14 |
15 |

Margin Break Outs

16 |

17 | The {`\`m{l|r|x}-break-out\``} classes are used to break out of 18 | the parent container and stretch to the full width of the window. These classes are all available using 19 | Tailwind's responsive system. 20 |

21 |
22 |
Container
23 |
mx-break-out
24 |
No break out
25 |
No break out
26 |
ml-break-out
27 |
mr-break-out
28 |
29 | mx-break-out 30 |

31 | Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam id dolor id 32 | nibh ultricies vehicula ut id elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Vivamus 33 | sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Sed posuere consectetur est at lobortis. 34 |

35 |
36 |
37 | 38 |
39 |

Padding Break Outs

40 |

41 | The {`\`p{l|r|x}-break-out\``} classes are used to counteract the 42 | {`\`m{l|r|x}-break-out\``} margin classes in order to align the 43 | inner content to the parent container. 44 |

45 | 46 |
47 |
Container
48 |
49 | mx-break-out, px-break-out 50 |

51 | Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam id dolor id 52 | nibh ultricies vehicula ut id elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Vivamus 53 | sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Sed posuere consectetur est at lobortis. 54 | Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam id dolor id 55 | nibh ultricies vehicula ut id elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Vivamus 56 | sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Sed posuere consectetur est at lobortis. 57 |

58 |
59 |
60 | ml-break-out, pl-break-out 61 |

62 | Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam id dolor id 63 | nibh ultricies vehicula ut id elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Vivamus 64 | sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Sed posuere consectetur est at lobortis. 65 |

66 |
67 |
68 | mr-break-out, pr-break-out 69 |

70 | Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam id dolor id 71 | nibh ultricies vehicula ut id elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Vivamus 72 | sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Sed posuere consectetur est at lobortis. 73 |

74 |
75 |
76 | 77 |
78 |

Max-Width & Dynamic Breakpoints

79 |

80 | The {`\`m{l|r|x}-break-out\``} margin classes and{' '} 81 | {`\`p{l|r|x}-break-out\``} classes used with{' '} 82 | max-* & min-*{' '} 83 | classes. 84 |

85 | 86 |
87 |
Container
88 |
89 | max-lg:mx-break-out, max-lg:px-break-out 90 |

91 | Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam id dolor id 92 | nibh ultricies vehicula ut id elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Vivamus 93 | sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Sed posuere consectetur est at lobortis. 94 | Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam id dolor id 95 | nibh ultricies vehicula ut id elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Vivamus 96 | sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Sed posuere consectetur est at lobortis. 97 |

98 |
99 |
100 |
101 |
102 | ); 103 | } 104 | -------------------------------------------------------------------------------- /src/tailwind-container-break-out.js: -------------------------------------------------------------------------------- 1 | const plugin = require('tailwindcss/plugin'); 2 | 3 | function normalizeScreens(screens, root = true) { 4 | if (Array.isArray(screens)) { 5 | return screens.map(screen => { 6 | if (root && Array.isArray(screen)) { 7 | throw new Error('The tuple syntax is not supported for `screens`.'); 8 | } 9 | 10 | if (typeof screen === 'string') { 11 | return { name: screen.toString(), values: [{ min: screen, max: undefined }] }; 12 | } 13 | 14 | let [name, options] = screen; 15 | name = name.toString(); 16 | 17 | if (typeof options === 'string') { 18 | return { name, values: [{ min: options, max: undefined }] }; 19 | } 20 | 21 | if (Array.isArray(options)) { 22 | return { name, values: options.map(option => resolveValue(option)) }; 23 | } 24 | 25 | return { name, values: [resolveValue(options)] }; 26 | }); 27 | } 28 | 29 | return normalizeScreens(Object.entries(screens ?? {}), false); 30 | } 31 | 32 | function resolveValue({ 'min-width': _minWidth, min = _minWidth, max, raw } = {}) { 33 | return { min, max, raw }; 34 | } 35 | 36 | function extractMinWidths(breakpoints = []) { 37 | return breakpoints 38 | .flatMap(breakpoint => breakpoint.values.map(breakpoint => breakpoint.min)) 39 | .filter(v => v !== undefined); 40 | } 41 | 42 | function mapMinWidthsToPadding(minWidths, screens, paddings) { 43 | if (typeof paddings === 'undefined') { 44 | return []; 45 | } 46 | 47 | if (!(typeof paddings === 'object' && paddings !== null)) { 48 | return [ 49 | { 50 | screen: 'DEFAULT', 51 | minWidth: 0, 52 | padding: paddings 53 | } 54 | ]; 55 | } 56 | 57 | let mapping = []; 58 | 59 | if (paddings.DEFAULT) { 60 | mapping.push({ 61 | screen: 'DEFAULT', 62 | minWidth: 0, 63 | padding: paddings.DEFAULT 64 | }); 65 | } 66 | 67 | let lastNonNullPadding = null; 68 | 69 | for (let minWidth of minWidths) { 70 | for (let screen of screens) { 71 | for (let { min } of screen.values) { 72 | if (min === minWidth) { 73 | const paddingForScreenName = paddings[screen.name]; 74 | if (paddingForScreenName !== undefined) { 75 | mapping.push({ minWidth, padding: paddingForScreenName }); 76 | lastNonNullPadding = paddingForScreenName; 77 | } else if (lastNonNullPadding !== null) { 78 | mapping.push({ minWidth, padding: lastNonNullPadding }); 79 | } 80 | } 81 | } 82 | } 83 | } 84 | 85 | return mapping; 86 | } 87 | 88 | module.exports = plugin(function ({ addComponents, addBase, theme }) { 89 | const screens = normalizeScreens(theme('container.screens', theme('screens'))); 90 | const minWidths = extractMinWidths(screens); 91 | const paddings = mapMinWidthsToPadding(minWidths, screens, theme('container.padding')); 92 | 93 | // TODO - account for non-centered containers. 94 | const generateMarginFor = (minWidth, xAxis) => { 95 | let paddingConfig; 96 | if (paddings.length === 1) { 97 | paddingConfig = paddings[0]; 98 | } else { 99 | paddingConfig = paddings.find(padding => `${padding.minWidth}` === `${minWidth}`); 100 | } 101 | 102 | // If the minWidth is zero (screen size is more than zero), there's no need to do complex calc. 103 | if (minWidth === 0) { 104 | if (!paddingConfig) { 105 | return {}; 106 | } 107 | if (xAxis === 'x') { 108 | return { 109 | marginLeft: `-${paddingConfig.padding}`, 110 | marginRight: `-${paddingConfig.padding}` 111 | }; 112 | } 113 | if (xAxis === 'r') { 114 | return { 115 | marginRight: `-${paddingConfig.padding}` 116 | }; 117 | } 118 | if (xAxis === 'l') { 119 | return { 120 | marginLeft: `-${paddingConfig.padding}` 121 | }; 122 | } 123 | } 124 | 125 | // If there is a minWidth, but there's no padding config, just do calc but don't worry about the padding. 126 | if (!paddingConfig) { 127 | if (xAxis === 'x') { 128 | return { 129 | marginLeft: `calc((-100vw + var(--twcb-scrollbar-width)) / 2 + ${minWidth} / 2 )`, 130 | marginRight: `calc((-100vw + var(--twcb-scrollbar-width)) / 2 + ${minWidth} / 2 )` 131 | }; 132 | } 133 | if (xAxis === 'r') { 134 | return { 135 | marginRight: `calc((-100vw + var(--twcb-scrollbar-width)) / 2 + ${minWidth} / 2 )` 136 | }; 137 | } 138 | if (xAxis === 'l') { 139 | return { 140 | marginLeft: `calc((-100vw + var(--twcb-scrollbar-width)) / 2 + ${minWidth} / 2 )` 141 | }; 142 | } 143 | } 144 | 145 | // If there is a padding config and there is a minWidth, do complex calc. 146 | if (xAxis === 'x') { 147 | return { 148 | marginLeft: `calc((-100vw + var(--twcb-scrollbar-width)) / 2 + ${minWidth} / 2 - ${paddingConfig.padding} )`, 149 | marginRight: `calc((-100vw + var(--twcb-scrollbar-width)) / 2 + ${minWidth} / 2 - ${paddingConfig.padding} )` 150 | }; 151 | } 152 | if (xAxis === 'r') { 153 | return { 154 | marginRight: `calc((-100vw + var(--twcb-scrollbar-width)) / 2 + ${minWidth} / 2 - ${paddingConfig.padding} )` 155 | }; 156 | } 157 | if (xAxis === 'l') { 158 | return { 159 | marginLeft: `calc((-100vw + var(--twcb-scrollbar-width)) / 2 + ${minWidth} / 2 - ${paddingConfig.padding} )` 160 | }; 161 | } 162 | }; 163 | 164 | const generatePaddingFor = (minWidth, xAxis) => { 165 | let paddingConfig; 166 | if (paddings.length === 1) { 167 | paddingConfig = paddings[0]; 168 | } else { 169 | paddingConfig = paddings.find(padding => `${padding.minWidth}` === `${minWidth}`); 170 | } 171 | 172 | if (minWidth === 0) { 173 | if (!paddingConfig) { 174 | return {}; 175 | } 176 | if (xAxis === 'x') { 177 | return { 178 | paddingLeft: `${paddingConfig.padding}`, 179 | paddingRight: `${paddingConfig.padding}` 180 | }; 181 | } 182 | if (xAxis === 'r') { 183 | return { 184 | paddingRight: `${paddingConfig.padding}` 185 | }; 186 | } 187 | if (xAxis === 'l') { 188 | return { 189 | paddingLeft: `${paddingConfig.padding}` 190 | }; 191 | } 192 | } 193 | 194 | if (!paddingConfig) { 195 | if (xAxis === 'x') { 196 | return { 197 | paddingLeft: `calc((100vw - var(--twcb-scrollbar-width)) / 2 - ${minWidth} / 2 )`, 198 | paddingRight: `calc((100vw - var(--twcb-scrollbar-width)) / 2 - ${minWidth} / 2 )` 199 | }; 200 | } 201 | if (xAxis === 'r') { 202 | return { 203 | paddingRight: `calc((100vw - var(--twcb-scrollbar-width)) / 2 - ${minWidth} / 2 )` 204 | }; 205 | } 206 | if (xAxis === 'l') { 207 | return { 208 | paddingLeft: `calc((100vw - var(--twcb-scrollbar-width)) / 2 - ${minWidth} / 2 )` 209 | }; 210 | } 211 | } 212 | 213 | if (xAxis === 'x') { 214 | return { 215 | paddingLeft: `calc((100vw - var(--twcb-scrollbar-width)) / 2 - ${minWidth} / 2 + ${paddingConfig.padding} )`, 216 | paddingRight: `calc((100vw - var(--twcb-scrollbar-width)) / 2 - ${minWidth} / 2 + ${paddingConfig.padding} )` 217 | }; 218 | } 219 | if (xAxis === 'r') { 220 | return { 221 | paddingRight: `calc((100vw - var(--twcb-scrollbar-width)) / 2 - ${minWidth} / 2 + ${paddingConfig.padding} )` 222 | }; 223 | } 224 | if (xAxis === 'l') { 225 | return { 226 | paddingLeft: `calc((100vw - var(--twcb-scrollbar-width)) / 2 - ${minWidth} / 2 + ${paddingConfig.padding} )` 227 | }; 228 | } 229 | }; 230 | 231 | const atRules = Array.from(new Set(minWidths.slice().sort((a, z) => parseInt(a) - parseInt(z)))).map( 232 | (minWidth, i) => { 233 | return { 234 | [`@media (min-width: ${minWidth})`]: { 235 | '.mx-break-out': { 236 | ...generateMarginFor(minWidth, 'x') 237 | }, 238 | '.ml-break-out': { 239 | ...generateMarginFor(minWidth, 'l') 240 | }, 241 | '.mr-break-out': { 242 | ...generateMarginFor(minWidth, 'r') 243 | }, 244 | '.px-break-out': { 245 | ...generatePaddingFor(minWidth, 'x') 246 | }, 247 | '.pl-break-out': { 248 | ...generatePaddingFor(minWidth, 'l') 249 | }, 250 | '.pr-break-out': { 251 | ...generatePaddingFor(minWidth, 'r') 252 | } 253 | } 254 | }; 255 | } 256 | ); 257 | 258 | addBase({ 259 | ':root': { 260 | '--twcb-scrollbar-width': '0px' 261 | } 262 | }); 263 | 264 | addComponents([ 265 | { 266 | '.mx-break-out': generateMarginFor(0, 'x') 267 | }, 268 | { 269 | '.ml-break-out': generateMarginFor(0, 'l') 270 | }, 271 | { 272 | '.mr-break-out': generateMarginFor(0, 'r') 273 | }, 274 | { 275 | '.px-break-out': generatePaddingFor(0, 'x') 276 | }, 277 | { 278 | '.pl-break-out': generatePaddingFor(0, 'l') 279 | }, 280 | { 281 | '.pr-break-out': generatePaddingFor(0, 'r') 282 | }, 283 | ...atRules 284 | ]); 285 | }); 286 | --------------------------------------------------------------------------------