UNPKG

17.5 kBMarkdownView Raw
1# @zaydek/duomo
2
3Duomo is a stack-based CSS framework. Contributions are welcome as issues and or pull requests.
4
5To get started, simply run this command:
6
7```bash
8yarn add @zaydek/duomo
9# or npm i @zaydek/duomo
10```
11
12**Usage: (CDN)**
13
14CodePen to get you started: https://codepen.io/zaydek/pen/ExgxjYy.
15
16```html
17<!DOCTYPE html>
18<html lang="en">
19 <head>
20 <meta charset="UTF-8" />
21 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
22 <title>Hello, world!</title>
23 <link rel="stylesheet" href="https://unpkg.com/@zaydek/duomo" />
24 </head>
25 <body>
26 <div class="hstack">
27 <div class="hstack space-16">
28 <div class="w-32 h-32 bg-gray-300 rounded-full"></div>
29 <div class="w-32 h-32 bg-gray-300 rounded-full"></div>
30 </div>
31 <div class="spacer"></div>
32 <div class="hstack space-16">
33 <div class="w-32 h-32 bg-gray-300 rounded-full"></div>
34 <div class="w-32 h-32 bg-gray-300 rounded-full"></div>
35 </div>
36 </div>
37 </body>
38</html>
39```
40
41**Usage (React):**
42
43```tsx
44import "@zaydek/duomo"
45
46function Component() {
47 return (
48 <div className="hstack">
49 <div className="hstack space-16">
50 <div className="w-32 h-32 bg-gray-300 rounded-full" />
51 <div className="w-32 h-32 bg-gray-300 rounded-full" />
52 </div>
53 <div className="spacer" />
54 <div className="hstack space-16">
55 <div className="w-32 h-32 bg-gray-300 rounded-full" />
56 <div className="w-32 h-32 bg-gray-300 rounded-full" />
57 </div>
58 </div>
59 )
60}
61```
62
63## Table of Contents:
64
65- [Why](#why)
66- [HStack](#hstack)
67- [VStack](#vstack)
68- [Spacer](#spacer)
69- [Spacing](#spacing)
70- [ZStack](#zstack)
71- [Range](#range)
72
73## Why
74
75Duomo is a spiritual successor of Tailwind CSS.
76
77Duomo benefits you in the following ways:
78
79- Small and intuitive API
80- Small JavaScript runtime for toggling dark mode, etc.
81- Built in debugging layout utilities
82- Soft landing for iOS / SwiftUI developers
83- Uses HStack and VStack instead of Flexbox for rows and columns
84- Uses ZStack to layer along the z-axis
85- Lightweight CDN footprint (<TODO>kb uncompressed, <TODO>kb with Brotli); no NPM installs required
86- No configuration needed
87- Sass API; easily extend Duomo when you install via NPM
88- Introspection via CSS variables; Duomo tokens can be overridden _without_ Sass
89- Maintenance-friendly HTML / CSS
90
91Reasons you might not want to use Duomo:
92
93- You like Tailwind CSS just fine thank you very much
94- You don’t want to use stacks vs. Flexbox\* (_stacks are based on Flexbox_)
95- You don’t have a need for small CDN footprints
96- Duomo is not 1.0 stable
97
98\*Note that stacks are exported as a standalone dependency; you can experiment with stacks _with_ any CSS library.
99
100## HStack
101
102HStacks `<div class="hstack">` stack children elements _horizontally_. Like `VStack`s, they can be modified with
103`align-{alignment}` and `space-{range}`. By default, children are automatically centered horizontally and vertically. To
104opt-out of horizontal centering, use a nested `<div class="spacer">`. To opt-out of vertical centering, use
105`align-stretch`, `align-start`, or `align-end`.
106
107**Spacer examples:**
108
109```html
110<!--
111 . . .
112 . x .
113 . . .
114-->
115<div class="hstack">
116 <div class="x"></div>
117</div>
118
119<!--
120 . . .
121 . . x
122 . . .
123-->
124<div class="hstack">
125 <div class="spacer"></div>
126 <div class="x"></div>
127</div>
128
129<!--
130 . . .
131 x . .
132 . . .
133-->
134<div class="hstack">
135 <div class="x"></div>
136 <div class="spacer"></div>
137</div>
138```
139
140**Alignment examples:**
141
142```html
143<!--
144 . x .
145 . x .
146 . x .
147-->
148<div class="hstack align-stretch">
149 <div class="x"></div>
150</div>
151
152<!--
153 . x .
154 . . .
155 . . .
156-->
157<div class="hstack align-start">
158 <div class="x"></div>
159</div>
160
161<!--
162 . . .
163 . . .
164 . x .
165-->
166<div class="hstack align-end">
167 <div class="x"></div>
168</div>
169```
170
171**Responsive examples:**
172
173Adapts to a vertical stack at the medium breakpoint.
174
175```html
176<div class="hstack md:vstack">
177 <!-- ... -->
178</div>
179```
180
181Stack is revealed at the medium breakpoint.\*
182
183_\*Not yet implemented._
184
185```html
186<div class="hidden md:vstack">
187 <!-- ... -->
188</div>
189```
190
191Stack is hidden at the medium breakpoint.\*
192
193_\*Not yet implemented._
194
195```html
196<div class="hstack md:hidden">
197 <!-- ... -->
198</div>
199```
200
201## VStack
202
203VStacks `<div class="vstack">` stack children elements _vertically_. Like `HStack`s, they can be modified with
204`align-{alignment}` and `space-{range}`. By default, children are automatically centered horizontally and vertically. To
205opt-out of horizontal centering, use a nested `<div class="spacer">`. To opt-out of horizontal centering, use
206`align-stretch`, `align-start`, or `align-end`.
207
208**Spacer examples:**
209
210```html
211<!--
212 . . .
213 . x .
214 . . .
215-->
216<div class="vstack">
217 <div class="x"></div>
218</div>
219
220<!--
221 . . .
222 . . .
223 . x .
224-->
225<div class="vstack">
226 <div class="spacer"></div>
227 <div class="x"></div>
228</div>
229
230<!--
231 . x .
232 . . .
233 . . .
234-->
235<div class="vstack">
236 <div class="x"></div>
237 <div class="spacer"></div>
238</div>
239```
240
241**Alignment examples:**
242
243```html
244<!--
245 . . .
246 x x x
247 . . .
248-->
249<div class="vstack align-stretch">
250 <div class="x"></div>
251</div>
252
253<!--
254 . . .
255 x . .
256 . . .
257-->
258<div class="vstack align-start">
259 <div class="x"></div>
260</div>
261
262<!--
263 . . .
264 . . x
265 . . .
266-->
267<div class="vstack align-end">
268 <div class="x"></div>
269</div>
270```
271
272**Responsive examples:**
273
274Adapts to a horizontal stack at the medium breakpoint.
275
276```html
277<div class="vstack md:hstack">
278 <!-- ... -->
279</div>
280```
281
282Stack is revealed at the medium breakpoint.\*
283
284_\*Not yet implemented._
285
286```html
287<div class="hidden md:vstack">
288 <!-- ... -->
289</div>
290```
291
292Stack is hidden at the medium breakpoint.\*
293
294_\*Not yet implemented._
295
296```html
297<div class="vstack md:hidden">
298 <!-- ... -->
299</div>
300```
301
302## Spacer
303
304Spacers `<div class="spacer">` create negative space between children elements. They can be used to simulate
305`justify-content: flex-start`, `justify-content: flex-end`, and `justify-content: space-evenly` declaratively.
306
307Spacers, unlike HStacks and VStacks, implement no modifiers.
308
309**Examples:**
310
311Simulates `justify-content: flex-start`.
312
313```html
314<!--
315 . . .
316 . x x
317 . . .
318-->
319<div class="hstack">
320 <div class="spacer"></div>
321 <div class="x"></div>
322 <div class="x"></div>
323</div>
324```
325
326Simulates `justify-content: flex-end`.
327
328```html
329<!--
330 . . .
331 x x .
332 . . .
333-->
334<div class="hstack">
335 <div class="x"></div>
336 <div class="x"></div>
337 <div class="spacer"></div>
338</div>
339```
340
341Simulates `justify-content: space-evenly`.
342
343```html
344<!--
345 . . . . .
346 . x . x .
347 . . . . .
348-->
349<div class="hstack">
350 <div class="spacer"></div>
351 <div class="x"></div>
352 <div class="spacer"></div>
353 <div class="x"></div>
354 <div class="spacer"></div>
355</div>
356```
357
358## Spacing
359
360The spacing modifier `<div class="[h|v]stack space-{px}>` creates consistent spacing between HStack and VStack children
361elements. Spacing can be any negative\* or positive range value.
362
363Note that spacing spaces children elements _horizontally_ or _vertically_ depending on the parent HStack or VStack
364context. **You don’t need to specifiy x-axis or y-axis-specific spacing**.
365
366_\*Not yet implemented._
367
368**Examples:**
369
370```html
371<div class="hstack space-8">
372 <div class="w-32 h-32 bg-gray-400 rounded-full"></div>
373 <!-- 8px gap -->
374 <div class="w-32 h-32 bg-gray-400 rounded-full"></div>
375 <!-- 8px gap -->
376 <div class="w-32 h-32 bg-gray-400 rounded-full"></div>
377 <!-- 8px gap -->
378 <div class="w-32 h-32 bg-gray-400 rounded-full"></div>
379</div>
380```
381
382```html
383<div class="vstack space-16">
384 <div class="w-32 h-32 bg-gray-400 rounded-full"></div>
385 <!-- 16px gap -->
386 <div class="w-32 h-32 bg-gray-400 rounded-full"></div>
387 <!-- 16px gap -->
388 <div class="w-32 h-32 bg-gray-400 rounded-full"></div>
389 <!-- 16px gap -->
390 <div class="w-32 h-32 bg-gray-400 rounded-full"></div>
391</div>
392```
393
394**Responsive examples:**
395
396```html
397<div class="hstack space-8 md:space-16">
398 <div class="w-32 h-32 bg-gray-400 rounded-full"></div>
399 <!-- 8px gap, 16px gap at the medium breakpoint -->
400 <div class="w-32 h-32 bg-gray-400 rounded-full"></div>
401 <!-- 8px gap, 16px gap at the medium breakpoint -->
402 <div class="w-32 h-32 bg-gray-400 rounded-full"></div>
403 <!-- 8px gap, 16px gap at the medium breakpoint -->
404 <div class="w-32 h-32 bg-gray-400 rounded-full"></div>
405</div>
406```
407
408## ZStack
409
410ZStacks `<div class="zstack">` stack children elements on the z-axis _and_ center children elements across the x- and y-
411axes. ZStacks make layering elements on top of one another easy. ZStacks are intended to be used _with_ HStacks and or
412VStacks; they simply layer and center children elements.
413
414ZStacks, unlike HStacks and VStacks, implement no modifiers.
415
416Note that ZStacks use `position: relative`, so children element that use `position: absolute` are bound to the parent
417ZStack bounding box.
418
419**Examples:**
420
421TODO
422
423<!--
424
425Layers a red, green, and blue element on top of each other. The blue element is visible. The red and green elements are obscured.
426
427```html
428<div class="zstack">
429 <div class="hstack w-32 h-32 bg-red-400 rounded-full"></div>
430 <div class="hstack w-32 h-32 bg-green-400 rounded-full"></div>
431 <div class="hstack w-32 h-32 bg-blue-400 rounded-full"></div>
432</div>
433```
434
435-->
436
437## Range
438
439The standard range describes legal range values. Some properties support negative ranges (`margin`) and some properties
440use a sub-range (`border`, etc.).
441
442**The standard range:**
443
444```
445 0-10 increments by 1 -> 0 1 2 3 4 5 6 7 8 9
446 10-20 increments by 2 -> 10 12 14 16 18
447 20-40 increments by 4 -> 20 24 28 32 36
448 40-80 increments by 8 -> 40 48 56 64 72
449 80-160 increments by 16 -> 80 96 112 128 144
450160-320 increments by 32 -> 160 192 224 256 288
451320-640 increments by 64 -> 320 384 448 512 576 640
452```
453
454<!--
455
456## Ranges
457
458Ranges describe all possible numerical values. Some properties support negative ranges (`margin`) and some properties use a sub-range (`border`, etc.).
459
460**Standard range:**
461
462```
463 0-10, increments by 1: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
464 10-20, increments by 2: 10, 12, 14, 16, 18
465 20-40, increments by 4: 20, 24, 28, 32, 36,
466 40-80, increments by 8: 40, 48, 56, 64, 72,
467 80-160, increments by 16: 80, 96, 112, 128, 144,
468160-320, increments by 32: 160, 192, 224, 256, 288, 320,
469```
470
471
472
473
474margin:
475
476m-{-range|range} margin-all
477mt- margin-top
478mr- margin-right
479mb- margin-bottom
480ml- margin-left
481mx- margin-x-axis
482my- margin-y-axis
483
484padding:
485
486p-{range} padding-all
487pt- padding-top
488pr- padding-right
489pb- padding-bottom
490pl- padding-left
491px- padding-x-axis
492py- padding-y-axis
493
494```
495
496-->
497
498<!--
499
500## Rationale and Demos
501
502If you’re asking yourself **‘Why did you build this from scratch and how is this different from Tailwind CSS?’**, check out these demos for a primer. Note that these are impromptu demos.
503
504<table>
505 <tr>
506 <td>
507 <a href="https://youtube.com/watch?v=4PMPkLHD7Os" target="_blank">
508 <img src="https://raw.githubusercontent.com/zaydek/duomo/main/.github/youtube-cover.png">
509 <p><strong>Demo 1: Introducing Stacks</strong></p>
510 </a>
511 </td>
512 <td>
513 <a href="https://youtube.com/watch?v=vV-A3ywo6v8" target="_blank">
514 <img src="https://raw.githubusercontent.com/zaydek/duomo/main/.github/youtube-cover.png">
515 <p><strong>Demo 2: Introducing Theming</strong></p>
516 </a>
517 </td>
518 </tr>
519</table>
520
521-->
522
523<!--
524
525## Table of Contents
526
527- [Stack-based layouts](#stack-based-layouts)
528- [Utility-first classes](#utility-first-classes)
529- [Emphasis on zero-configuration](#zero-configuration)
530- [Introspection via CSS variables (custom properties)](#introspection)
531
532## [Stack-Based Layouts](#stack-based-layouts)
533
534<sub>Stacks are inspired by [Swift UI](https://developer.apple.com/videos/play/wwdc2020/10031) and [Jon Q’s talk about implementing Swift UI stacks in CSS](https://youtube.com/watch?v=fvOlTDJpNcM).</sub>
535
536What are stack-based layouts? Instead of thinking in terms of Flexbox, think in terms of stacks. A stack simply describes a horizontal or vertical axis, and stacks compose a layout. It’s turtles all the way down, for stacks. 🐢<sub>🐢</sub>
537
538Why stacks? Stacks are a more natural way of thinking about layout. The trouble with Flexbox is that you need to remember `display`, `flex-direction`, `justify-content`, `align-items`, and `flex`, and remember how these properties change in the context of `flex-direction: row` and `flex-direction: column`. Stacks are a much more simple but powerful primitive for describing layout _that is based on Flexbox_.
539
540This is a microcosm of how Duomo works:
541
542```scss
543.hstack {
544 display: flex;
545 flex-direction: row;
546 justify-content: center;
547}
548.hstack > * + * {
549 margin-top: 0;
550 margin-left: var(--space, 0);
551}
552
553.vstack {
554 display: flex;
555 flex-direction: column;
556 justify-content: center;
557}
558.vstack > * + * {
559 margin-left: 0;
560 margin-top: var(--space, 0);
561}
562
563.spacer {
564 flex: 1 0 var(--space, 0);
565}
566```
567
568-->
569
570<!--
571
572And this is a macrocosm of how Duomo works:
573
574```scss
575$separator: "\\:";
576
577@function px($value) {
578 @return $value + px;
579}
580
581@function rem($value) {
582 @return $value / 16 + rem;
583}
584
585@function get-dynamic-ampersand() {
586 @if not & {
587 @return ".";
588 }
589 @return & + $separator;
590}
591
592@mixin stacks {
593 $amp: get-dynamic-ampersand();
594
595 #{$amp}hstack {
596 display: flex;
597 flex-direction: row;
598 justify-content: center;
599 align-items: stretch;
600 }
601 // NOTE: The root stack context resets `--space`.
602 @if not & {
603 .hstack > * {
604 --space: 0;
605 }
606 }
607 #{$amp}hstack > * + * {
608 margin-top: 0;
609 margin-left: var(--space, 0);
610 }
611 // NOTE: Omit `spacer`s and sibling elements from `var(--space)`.
612 #{$amp}hstack > .spacer:empty,
613 #{$amp}hstack > .spacer:empty + * {
614 margin-left: 0;
615 }
616
617 #{$amp}vstack {
618 display: flex;
619 flex-direction: column;
620 justify-content: center;
621 align-items: stretch;
622 }
623 // NOTE: The root stack context resets `--space`.
624 @if not & {
625 .vstack > * {
626 --space: 0;
627 }
628 }
629 #{$amp}vstack > * + * {
630 margin-left: 0;
631 margin-top: var(--space, 0);
632 }
633 // NOTE: Omit `spacer`s and sibling elements from `var(--space)`.
634 #{$amp}vstack > .spacer:empty,
635 #{$amp}vstack > .spacer:empty + * {
636 margin-top: 0;
637 }
638
639 #{$amp}align-start {
640 align-items: flex-start;
641 }
642 #{$amp}align-center {
643 align-items: center;
644 }
645 #{$amp}align-end {
646 align-items: flex-end;
647 }
648
649 @if not & {
650 .spacer {
651 flex-grow: 1;
652 flex-shrink: 0;
653 flex-basis: var(--space, 0);
654 }
655 // NOTE: Edge `spacer`s are collapsible to `0`.
656 .spacer:first-child,
657 .spacer:last-child {
658 flex-basis: 0;
659 }
660 }
661
662 @each $value in (4, 8, 12, 16, 20, 24, 28, 32) {
663 #{$amp}space-#{$value} > * {
664 --space: #{rem($value)};
665 }
666 }
667}
668
669@mixin reset {
670 *,
671 *::before,
672 *::after {
673 box-sizing: border-box;
674 }
675
676 body {
677 margin: 0;
678 }
679}
680
681@mixin debugger {
682 * {
683 outline: 2px solid hsla(215, 100%, 50%, 0.2);
684 outline-offset: -1px;
685 }
686}
687
688@at-root {
689 @include reset;
690 @include debugger;
691 @include stacks;
692 @each $key, $value in ("sm": 640, "md": 768, "lg": 1024, "xl": 1280) {
693 @media (min-width: #{px($value)}) {
694 .#{$key} {
695 @at-root {
696 @include stacks;
697 }
698 }
699 }
700 }
701}
702```
703
704This is how Duomo works. Duomo includes a CSS reset, debugger, and many utility classes that follow the same naming conventions as Tailwind CSS. Stacks however are the core of _how_ and _why_ Duomo works.
705
706-->
707
708<!--
709
710- `hstack`s implements a horizontal stack. Think `flex-direction: row`.
711- `vstack`s implements a vertical stack. Think `flex-direction: column`.
712- `spacer`s implements direction-agnostic spacers. Think `flex: 1`.
713
714Stacks in Duomo are easy to reason about because they manage Flexbox for you. 💡 Furthermore, Duomo stacks cover edge cases such as every stack resets `--space` and `spacer`s shrink to `--space` (unless they are the start or end element). **This enables you to think declaratively without worrying about implementation details or corner cases.**
715
716## [Utility-First Classes](#utility-first-classes)
717
718TODO
719
720## [Zero-Configuration](#zero-configuration)
721
722TODO
723
724## [Introspection](#introspection)
725
726TODO
727
728-->
729
730## License
731
732Licensed as [MIT](./LICENSE).
733
734<!--
735
736Duomo is a utility-first framework for rapid development. Duomo is most similar to Tailwind CSS but differentiates itself in these key ways:
737
738- Emphasis on zero-configuration
739- SCSS-based, designed to be extended
740- Limited API set, oriented for responsive skeleton prototyping
741- Intended to be used in conjunction with CSS or SCSS
742- Multiple CDN links for prototyping, add breakpoints as needed
743- Classes map to utilities but can encompass higher-order patterns like `m-gap`
744
745Furthermore, Duomo is simpler than Tailwind CSS for the following reasons:
746
747- The API surface area is dramatically smaller
748- More consistent class
749- Numerical values are specified using `px` equivalents, not Tailwind units
750- There are intentionally no `hover:`, `focus:`, etc. pseudo classes
751- You are encouraged to use Duomo and CSS or SCSS, not exclusively Tailwind CSS or Duomo
752
753The problem with almost every CSS library and framework is they attempt to _solve_ frontend. Frontend is not a solvable problem. Frontend is the amalgamation of _many_ tools coming together to create user experiences for people. How Duomo fits into this equation is by making it dead-easy to prototype responsive skeletons.
754
755The key to understanding Duomo is to understand that it **does not attempt** to map every CSS property to an arbitrary class. What Duomo does is makes it easier to reason about responsive skeletons, and encourages you to lean into CSS or SCSS when you’re ready to retrofit your skeleton-app with content.
756
757-->
758
\No newline at end of file