├── .gitignore
├── README.md
├── demo-src
├── eleventy.config.js
├── index.njk
└── prism-theme.css
├── flex-luthor-debug-fill.css
├── flex-luthor-separators.css
├── flex-luthor.css
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | package-lock.json
3 | _site
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Flex Luthor
2 |
3 | A small wrapper library to help with responsive intrinsic-sized Flexbox layouts that wrap based on content and container width (avoiding viewport-based media queries when possible).
4 |
5 | ## Demo
6 |
7 | [https://flex-luthor.zachleat.dev/](https://flex-luthor.zachleat.dev/)
8 |
9 | ## Installation
10 |
11 | Available on [npm](https://www.npmjs.com/package/@zachleat/flex-luthor).
12 |
13 | ```
14 | npm install @zachleat/flex-luthor --save
15 | ```
16 |
17 | ## Versions
18 |
19 | * `v4.0.0`: Margin feature renamed more accurately to Gap: `--fl-margin` is now `--fl-gap`. Gap now defaults to `.5em 1em` (`.5em` vertical, `1em` horizontal). You can reset this to `0` if needed.
20 | * `v3.0.0`: Forked from [`filamentgroup/flex-luthor`](https://github.com/filamentgroup/flex-luthor/) and updated to support Flexbox with `gap`.
--------------------------------------------------------------------------------
/demo-src/eleventy.config.js:
--------------------------------------------------------------------------------
1 | const syntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight");
2 | const { pairedShortcode } = require("@11ty/eleventy-plugin-syntaxhighlight");
3 |
4 | module.exports = function(eleventyConfig) {
5 | eleventyConfig.addPlugin(syntaxHighlight);
6 |
7 | eleventyConfig.addPassthroughCopy("*.css");
8 | eleventyConfig.addPassthroughCopy("demo-src/*.css");
9 | eleventyConfig.setServerOptions({
10 | domDiff: false
11 | });
12 |
13 | eleventyConfig.addPairedShortcode("example", function(content, args) {
14 | let [language, ...highlightNumbers] = args.split(" ");
15 |
16 | return `
17 | ${content}
18 |
19 |
20 | View Source
21 | ${pairedShortcode(content, language, highlightNumbers.join(" "))}
22 | `;
23 | });
24 | };
--------------------------------------------------------------------------------
/demo-src/index.njk:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | The Flex Luthor Flexbox Layout System
7 |
8 |
9 |
10 |
11 |
89 |
90 |
91 |
92 |
93 |
94 | The Flex Luthor Flexbox Layout System
95 | Alternatively known as Oh no why did they make an abstraction for Flexbox . View on GitHub .
96 |
97 |
98 | Simple Layouts
99 | Two Column
100 | {% example "html" %}
101 |
102 |
First cell
103 |
Second cell
104 |
105 | {% endexample %}
106 |
107 | Five Column
108 | Adding more cells divides the container evenly.
109 |
110 | {% example "html" %}
111 |
112 |
First cell
113 |
Second cell
114 |
Third cell
115 |
Fourth cell
116 |
Fifth cell
117 |
118 | {% endexample %}
119 |
120 | Gap Between Cells
121 |
122 | Gaps are defaulted to 1em
horizontal and .5em
vertical. They only show between cells. Horizontal gaps are not shown when cells are all stacked. Vertical gaps are not shown when cells are in a single row.
123 |
124 | Horizontal (--fl-gap-h: 2em
)
125 | {% example "html" %}
126 |
127 |
First cell
128 |
Second cell
129 |
Third cell
130 |
Fourth cell
131 |
Fifth cell
132 |
133 | {% endexample %}
134 |
135 | Vertical (--fl-gap-h: 1em
)
136 | {% example "html" %}
137 |
138 |
First cell
139 |
Second cell
140 |
Third cell
141 |
Fourth cell
142 |
Fifth cell
143 |
144 | {% endexample %}
145 |
146 | Both Vertical and Horizontal (--fl-gap-h: 2em; --fl-gap-v: 1em
)
147 | {% example "html" %}
148 |
149 |
First cell
150 |
Second cell
151 |
Third cell
152 |
Fourth cell
153 |
Fifth cell
154 |
155 | {% endexample %}
156 |
157 | Stackpoint
158 |
159 | This is the Flexbox Holy Abatross method from Heydon Pickering . This is a container width based breakpoint. Going below this width forces the layout to a single column.
160 |
161 | Single Column below 27em
(432px
) Container Width
162 | {% example "html" %}
163 |
164 |
First cell
165 |
Second cell
166 |
Third cell
167 |
Fourth cell
168 |
Fifth cell
169 |
170 | {% endexample %}
171 |
172 |
173 | Separators
174 |
175 | Separators only show between cells between columns and rows. If all cells are stacked in a single column, no horizontal separators will be shown. Same for vertical separators and a single row of cells.
176 |
177 | Horizontal
178 | {% example "html" %}
179 |
180 |
First cell
181 |
Second cell
182 |
Third cell
183 |
Fourth cell
184 |
Fifth cell
185 |
186 | {% endexample %}
187 |
188 | Vertical
189 | {% example "html" %}
190 |
191 |
First cell
192 |
Second cell
193 |
Third cell
194 |
Fourth cell
195 |
Fifth cell
196 |
197 | {% endexample %}
198 |
199 | Both Horizontal and Vertical
200 | {% example "html" %}
201 |
202 |
First cell
203 |
Second cell
204 |
Third cell
205 |
Fourth cell
206 |
Fifth cell
207 |
208 | {% endexample %}
209 |
210 | Both Horizontal and Vertical (`rtl` direction)
211 | {% example "html" %}
212 |
213 |
First cell
214 |
Second cell
215 |
Third cell
216 |
Fourth cell
217 |
Fifth cell
218 |
219 | {% endexample %}
220 |
221 | Prefer to Wrap Cell Before Content
222 |
223 | By default, the content inside of a cell will wrap before the cell will wrap to a new line. To swap this so that the cell will wrap before the content wraps, add the flex-cell-wrap
.
224 |
225 | {% example "html" %}
226 |
227 |
First cell
228 |
This cell will wrap to a new line before wrapping its content
229 |
Third cell
230 |
Fourth cell
231 |
Fifth cell
232 |
233 | {% endexample %}
234 |
235 | You can apply this to all cells in the layout with fl-nowrap
on the container.
236 |
237 | {% example "html" %}
238 |
239 |
First cell
240 |
This cell will wrap to a new line before wrapping its content
241 |
Third cell
242 |
Fourth cell
243 |
Fifth cell
244 |
245 | {% endexample %}
246 |
247 | Inline Layout
248 |
249 | Cells only take up their content width. This disables flex-grow
on the cells. Works with fl-cell-wrap
too.
250 |
251 | {% example "html" %}
252 |
253 |
First cell
254 |
This cell will wrap to a new line before wrapping its content
255 |
Third cell
256 |
Fourth cell
257 |
Fifth cell
258 |
259 | {% endexample %}
260 |
261 |
262 |
--------------------------------------------------------------------------------
/demo-src/prism-theme.css:
--------------------------------------------------------------------------------
1 | /**
2 | * xonokai theme for JavaScript, CSS and HTML
3 | * based on: https://github.com/MoOx/sass-prism-theme-base by Maxime Thirouin ~ MoOx --> http://moox.fr/ , which is Loosely based on Monokai textmate theme by http://www.monokai.nl/
4 | * license: MIT; http://moox.mit-license.org/
5 | */
6 | code[class*="language-"],
7 | pre[class*="language-"] {
8 | -moz-tab-size: 2;
9 | -o-tab-size: 2;
10 | tab-size: 2;
11 | -webkit-hyphens: none;
12 | -moz-hyphens: none;
13 | -ms-hyphens: none;
14 | hyphens: none;
15 | white-space: pre;
16 | white-space: pre-wrap;
17 | word-wrap: normal;
18 | font-family: Menlo, Monaco, "Courier New", monospace;
19 | font-size: 14px;
20 | color: #76d9e6;
21 | text-shadow: none;
22 | }
23 |
24 | pre > code[class*="language-"] {
25 | font-size: 1em;
26 | }
27 |
28 | pre[class*="language-"],
29 | :not(pre) > code[class*="language-"] {
30 | background: #2a2a2a;
31 | }
32 |
33 | pre[class*="language-"] {
34 | padding: 15px;
35 | border-radius: 4px;
36 | border: 1px solid #e1e1e8;
37 | overflow: auto;
38 | position: relative;
39 | }
40 |
41 | pre[class*="language-"] code {
42 | white-space: pre;
43 | display: block;
44 | }
45 |
46 | :not(pre) > code[class*="language-"] {
47 | padding: 0.15em 0.2em 0.05em;
48 | border-radius: .3em;
49 | border: 0.13em solid #7a6652;
50 | box-shadow: 1px 1px 0.3em -0.1em #000 inset;
51 | }
52 |
53 | .token.namespace {
54 | opacity: .7;
55 | }
56 |
57 | .token.comment,
58 | .token.prolog,
59 | .token.doctype,
60 | .token.cdata {
61 | color: #6f705e;
62 | }
63 |
64 | .token.operator,
65 | .token.boolean,
66 | .token.number {
67 | color: #a77afe;
68 | }
69 |
70 | .token.attr-name,
71 | .token.string {
72 | color: #e6d06c;
73 | }
74 |
75 | .token.entity,
76 | .token.url,
77 | .language-css .token.string,
78 | .style .token.string {
79 | color: #e6d06c;
80 | }
81 |
82 | .token.selector,
83 | .token.inserted {
84 | color: #a6e22d;
85 | }
86 |
87 | .token.atrule,
88 | .token.attr-value,
89 | .token.keyword,
90 | .token.important,
91 | .token.deleted {
92 | color: #ef3b7d;
93 | }
94 |
95 | .token.regex,
96 | .token.statement {
97 | color: #76d9e6;
98 | }
99 |
100 | .token.placeholder,
101 | .token.variable {
102 | color: #fff;
103 | }
104 |
105 | .token.important,
106 | .token.statement,
107 | .token.bold {
108 | font-weight: bold;
109 | }
110 |
111 | .token.punctuation {
112 | color: #bebec5;
113 | }
114 |
115 | .token.entity {
116 | cursor: help;
117 | }
118 |
119 | .token.italic {
120 | font-style: italic;
121 | }
122 |
123 | code.language-markup {
124 | color: #f9f9f9;
125 | }
126 |
127 | code.language-markup .token.tag {
128 | color: #ef3b7d;
129 | }
130 |
131 | code.language-markup .token.attr-name {
132 | color: #a6e22d;
133 | }
134 |
135 | code.language-markup .token.attr-value {
136 | color: #e6d06c;
137 | }
138 |
139 | code.language-markup .token.style,
140 | code.language-markup .token.script {
141 | color: #76d9e6;
142 | }
143 |
144 | code.language-markup .token.script .token.keyword {
145 | color: #76d9e6;
146 | }
147 |
148 | /* Line highlight plugin */
149 | pre[class*="language-"][data-line] {
150 | position: relative;
151 | padding: 1em 0 1em 3em;
152 | }
153 |
154 | pre[data-line] .line-highlight {
155 | position: absolute;
156 | left: 0;
157 | right: 0;
158 | padding: 0;
159 | margin-top: 1em;
160 | background: rgba(255, 255, 255, 0.08);
161 | pointer-events: none;
162 | line-height: inherit;
163 | white-space: pre;
164 | }
165 |
166 | pre[data-line] .line-highlight:before,
167 | pre[data-line] .line-highlight[data-end]:after {
168 | content: attr(data-start);
169 | position: absolute;
170 | top: .4em;
171 | left: .6em;
172 | min-width: 1em;
173 | padding: 0.2em 0.5em;
174 | background-color: rgba(255, 255, 255, 0.4);
175 | color: black;
176 | font: bold 65%/1 sans-serif;
177 | height: 1em;
178 | line-height: 1em;
179 | text-align: center;
180 | border-radius: 999px;
181 | text-shadow: none;
182 | box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);
183 | }
184 |
185 | pre[data-line] .line-highlight[data-end]:after {
186 | content: attr(data-end);
187 | top: auto;
188 | bottom: .4em;
189 | }
--------------------------------------------------------------------------------
/flex-luthor-debug-fill.css:
--------------------------------------------------------------------------------
1 | /* Debug Background colors */
2 | .fl-debug > :nth-child(6n) {
3 | background-color: #fea3aa;
4 | }
5 | .fl-debug > :nth-child(6n+1) {
6 | background-color: #f8b88b;
7 | }
8 | .fl-debug > :nth-child(6n+2) {
9 | background-color: #faf884;
10 | }
11 | .fl-debug > :nth-child(6n+3) {
12 | background-color: #baed91;
13 | }
14 | .fl-debug > :nth-child(6n+4) {
15 | background-color: #b2cefe;
16 | }
17 | .fl-debug > :nth-child(6n+5) {
18 | background-color: #f2a2e8;
19 | }
20 |
--------------------------------------------------------------------------------
/flex-luthor-separators.css:
--------------------------------------------------------------------------------
1 | /* Separators */
2 | @supports (clip-path: inset(0px 0px)) or (-webkit-clip-path: inset(0px 0px)) {
3 | .fl-separator-v,
4 | .fl-separator-h {
5 | -webkit-clip-path: inset(0 0 0 0);
6 | clip-path: inset(0 0 0 0);
7 | }
8 | .fl-separator-v > *,
9 | .fl-separator-h > * {
10 | position: relative;
11 | }
12 | .fl-separator-h > *:before {
13 | content: "";
14 | position: absolute;
15 | left: calc(-.5 * var(--fl-gap-h, 0) - .5px);
16 | top: 0;
17 | bottom: 0;
18 | border-left: 1px solid var(--fl-separator-h-color, var(--fl-separator-color, rgba(162,208,239,.35)));
19 | }
20 | .fl-separator-v > *:after {
21 | content: "";
22 | position: absolute;
23 | left: 0;
24 | top: calc(-.5 * var(--fl-gap-v, 0) - 1px);
25 | right: 0;
26 | border-top: 1px solid var(--fl-separator-v-color, var(--fl-separator-color, rgba(162,208,239,.35)));
27 | }
28 | .fl-separator-v *:before {
29 | top: calc(-.5 * var(--fl-gap-h, 0));
30 | bottom: calc(-.5 * var(--fl-gap-h, 0) + 1px);
31 | }
32 | .fl-separator-v > *:after {
33 | left: calc(-1 * var(--fl-gap-v, 0));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/flex-luthor.css:
--------------------------------------------------------------------------------
1 | /* Layout component code */
2 | .fl {
3 | /* The custom properties should not inherit from parent layouts */
4 | --fl-stackpoint: initial;
5 | --fl-gap-h: 1em; /* This needs to have a unit for calc() below */
6 | --fl-gap-v: .5em; /* This needs to have a unit for calc() below */
7 | display: flex;
8 | flex-wrap: wrap;
9 | gap: var(--fl-gap-v, 0px) var(--fl-gap-h, 0px);
10 | }
11 | .fl > * {
12 | /* if a margin is in play, account for this in our stackpoint calculations */
13 | /* notably this will also work if a stackpoint is not used. */
14 | --fl-gap-mod: calc( var(--fl-stackpoint, -1 * var(--fl-gap-h, 0px)) - var(--fl-stackpoint, 0px) );
15 | --fl-stackpoint-calc: calc( ( var(--fl-stackpoint, 100%) - 100% + var(--fl-gap-mod, 0px) ) * 999);
16 |
17 | flex-grow: 1;
18 | /* This will always have a value, due to CSS variable defaults set in .fl above */
19 | flex-basis: var(--fl-stackpoint-calc);
20 | }
21 | /* Prefer to wrap cells before content */
22 | .fl-nowrap > *,
23 | .fl-cell-wrap { /* or for an individual cell */
24 | flex-basis: auto;
25 | }
26 |
27 | /* Flex layout inline */
28 | .fl-inline > * {
29 | flex-grow: 0;
30 | }
31 |
32 | /* Debug */
33 | .fl-debug > * {
34 | outline: 1px dotted rgba(0,0,0,.4);
35 | outline-offset: -1px;
36 | }
37 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@zachleat/flex-luthor",
3 | "version": "4.0.2",
4 | "description": "A small wrapper library to help with responsive intrinsic-sized Flexbox layouts that wrap based on content and container width and avoiding viewport-based media queries.",
5 | "main": "flex-luthor.css",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build": "npx @11ty/eleventy --config=demo-src/eleventy.config.js --input=demo-src",
11 | "start": "npm run build -- --serve"
12 | },
13 | "keywords": [
14 | "responsive",
15 | "flexbox",
16 | "intrinsic",
17 | "css"
18 | ],
19 | "author": {
20 | "name": "Zach Leatherman",
21 | "email": "zachleatherman@gmail.com",
22 | "url": "https://zachleat.com/"
23 | },
24 | "license": "MIT",
25 | "devDependencies": {
26 | "@11ty/eleventy": "^2.0.0",
27 | "@11ty/eleventy-plugin-syntaxhighlight": "^4.2.0"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------