@fluid-theme/core 0.4.3 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/AGENTS.md +6 -29
  2. package/CHANGELOG.md +564 -2
  3. package/CLAUDE.md +201 -14
  4. package/GEMINI.md +6 -45
  5. package/README.md +17 -8
  6. package/assets/js/compare.js +77 -0
  7. package/assets/js/fit-text.js +103 -0
  8. package/assets/js/lightbox.js +153 -0
  9. package/assets/js/scroll-reveal.js +72 -0
  10. package/assets/js/toc-build.mjs +86 -0
  11. package/assets/js/toc.js +384 -0
  12. package/dist/fluid-1x.min.css +4 -0
  13. package/dist/fluid-fonts-archivo.min.css +2 -0
  14. package/dist/fluid-fonts-bodoni.min.css +2 -0
  15. package/dist/fluid-fonts.min.css +2 -0
  16. package/dist/fluid-legacy-1x.css +3 -0
  17. package/dist/fluid-nav.min.css +3 -0
  18. package/dist/fluid-toc.min.css +3 -0
  19. package/dist/fluid-zero.min.css +3 -0
  20. package/dist/fluid.min.css +3 -2
  21. package/dist/fonts/OFL-archivo.txt +93 -0
  22. package/dist/fonts/OFL-bodoni-moda.txt +93 -0
  23. package/dist/fonts/OFL-hanken-grotesk.txt +93 -0
  24. package/dist/fonts/OFL-newsreader.txt +93 -0
  25. package/dist/fonts/OFL-schibsted-grotesk.txt +93 -0
  26. package/dist/fonts/archivo-latin-wdth-italic.woff2 +0 -0
  27. package/dist/fonts/archivo-latin-wdth-normal.woff2 +0 -0
  28. package/dist/fonts/bodoni-moda-latin-opsz-italic.woff2 +0 -0
  29. package/dist/fonts/bodoni-moda-latin-opsz-normal.woff2 +0 -0
  30. package/dist/fonts/hanken-grotesk-latin-wght-italic.woff2 +0 -0
  31. package/dist/fonts/hanken-grotesk-latin-wght-normal.woff2 +0 -0
  32. package/dist/fonts/newsreader-latin-opsz-italic.woff2 +0 -0
  33. package/dist/fonts/newsreader-latin-opsz-normal.woff2 +0 -0
  34. package/dist/fonts/schibsted-grotesk-latin-wght-italic.woff2 +0 -0
  35. package/dist/fonts/schibsted-grotesk-latin-wght-normal.woff2 +0 -0
  36. package/fluid-agent-kit/README.md +1 -0
  37. package/fluid-agent-kit/skills/accessibility.md +3 -1
  38. package/fluid-agent-kit/skills/css-conventions.md +22 -5
  39. package/fluid-agent-kit/skills/image-optimization.md +2 -0
  40. package/fluid-agent-kit/skills/preset-composition.md +108 -0
  41. package/package.json +36 -11
  42. package/src/animations/scroll-reveal.css +75 -193
  43. package/src/components/accordion.css +6 -47
  44. package/src/components/alert.css +47 -88
  45. package/src/components/avatar.css +4 -20
  46. package/src/components/badge.css +25 -48
  47. package/src/components/button.css +69 -175
  48. package/src/components/card.css +146 -481
  49. package/src/components/chip.css +46 -12
  50. package/src/components/compare.css +234 -0
  51. package/src/components/dropdown.css +10 -67
  52. package/src/components/empty-state.css +4 -0
  53. package/src/components/form.css +113 -81
  54. package/src/components/gallery.css +252 -0
  55. package/src/components/hero.css +203 -88
  56. package/src/components/indicator.css +23 -117
  57. package/src/components/loading.css +7 -29
  58. package/src/components/modal.css +12 -65
  59. package/src/components/nav-modes.css +261 -0
  60. package/src/components/nav.css +324 -392
  61. package/src/components/search-input.css +4 -2
  62. package/src/components/step-indicator.css +26 -27
  63. package/src/components/table.css +64 -171
  64. package/src/components/toc.css +254 -0
  65. package/src/components/tooltip.css +5 -46
  66. package/src/core/base.css +393 -547
  67. package/src/core/reset.css +17 -5
  68. package/src/core/tokens.css +426 -2276
  69. package/src/fluid.css +258 -17
  70. package/src/fonts/OFL-newsreader.txt +93 -0
  71. package/src/fonts/OFL-schibsted-grotesk.txt +93 -0
  72. package/src/fonts/fluid-fonts.css +55 -0
  73. package/src/fonts/newsreader-latin-opsz-italic.woff2 +0 -0
  74. package/src/fonts/newsreader-latin-opsz-normal.woff2 +0 -0
  75. package/src/fonts/schibsted-grotesk-latin-wght-italic.woff2 +0 -0
  76. package/src/fonts/schibsted-grotesk-latin-wght-normal.woff2 +0 -0
  77. package/src/layouts/positioning.css +6 -3
  78. package/src/legacy-1x.css +61 -0
  79. package/src/sections/cta.css +33 -101
  80. package/src/sections/features.css +42 -219
  81. package/src/sections/footer.css +53 -134
  82. package/src/sections/stats.css +40 -153
  83. package/src/sections/team.css +24 -113
  84. package/src/sections/testimonials.css +34 -148
  85. package/src/zero/base.css +166 -0
  86. package/src/zero/tokens.css +64 -0
  87. package/src/zero.css +20 -0
  88. package/starters/workflows/README.md +40 -0
  89. package/starters/workflows/dependabot.yml +21 -0
  90. package/starters/workflows/update-fluid-theme.yml +296 -0
  91. package/themes/fonts/OFL-archivo.txt +93 -0
  92. package/themes/fonts/OFL-bodoni-moda.txt +93 -0
  93. package/themes/fonts/OFL-hanken-grotesk.txt +93 -0
  94. package/themes/fonts/archivo-latin-wdth-italic.woff2 +0 -0
  95. package/themes/fonts/archivo-latin-wdth-normal.woff2 +0 -0
  96. package/themes/fonts/archivo.css +20 -0
  97. package/themes/fonts/bodoni-moda-latin-opsz-italic.woff2 +0 -0
  98. package/themes/fonts/bodoni-moda-latin-opsz-normal.woff2 +0 -0
  99. package/themes/fonts/bodoni.css +35 -0
  100. package/themes/fonts/hanken-grotesk-latin-wght-italic.woff2 +0 -0
  101. package/themes/fonts/hanken-grotesk-latin-wght-normal.woff2 +0 -0
  102. package/themes/gallery.css +105 -0
  103. package/themes/swiss.css +53 -0
  104. package/src/components/scroll-progress.css +0 -104
  105. package/src/components/settings-panel.css +0 -603
  106. package/src/components/themed-images.css +0 -229
  107. package/src/components/version-badge.css +0 -70
@@ -13,7 +13,7 @@
13
13
  * </header>
14
14
  * <div class="team__grid">
15
15
  * <article class="team-member">
16
- * <img class="avatar avatar--xl" src="..." alt="Jane Doe">
16
+ * <img class="avatar" data-size="xl" src="..." alt="Jane Doe">
17
17
  * <h3 class="team-member__name">Jane Doe</h3>
18
18
  * <p class="team-member__role">CEO & Co-founder</p>
19
19
  * <p class="team-member__bio">Short bio about Jane.</p>
@@ -28,154 +28,65 @@
28
28
 
29
29
  @layer components {
30
30
 
31
- /* ========================================
32
- BASE TEAM SECTION
33
- ======================================== */
31
+ /* The band's padding and edges, and the header (a section head), are in
32
+ src/fluid.css (layouts). */
34
33
 
35
- .team {
36
- padding-block: var(--space-16);
37
- padding-inline: var(--space-5);
38
- max-inline-size: var(--container-xl);
39
- margin-inline: auto;
40
- }
41
-
42
- .team__header {
43
- text-align: center;
44
- max-inline-size: var(--container-md);
45
- margin-inline: auto;
46
- margin-block-end: var(--space-12);
47
- }
48
-
49
- .team__header h2 {
50
- font-size: var(--font-size-3xl);
51
- font-weight: var(--weight-bold);
52
- color: var(--color-text);
53
- margin-block-end: var(--space-3);
54
- }
55
-
56
- .team__header p {
57
- font-size: var(--font-size-lg);
58
- color: var(--color-text-muted);
59
- }
60
-
61
-
62
- /* ========================================
63
- TEAM GRID
64
- ======================================== */
34
+ .team__header p:not(.eyebrow, .lede) { color: var(--color-text-muted); } /* the label voice owns the eyebrow's colour */
65
35
 
66
36
  .team__grid {
67
37
  display: grid;
68
- gap: var(--space-8);
69
- grid-template-columns: repeat(
70
- auto-fit,
71
- minmax(min(100%, 14rem), 1fr)
72
- );
38
+ gap: var(--space-l) var(--space-m);
39
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
73
40
  }
74
41
 
75
-
76
- /* ========================================
77
- TEAM MEMBER CARD
78
- ======================================== */
79
-
42
+ /* A member is open, like a card: avatar or photo, name, role, bio. */
80
43
  .team-member {
81
- --_member-bg: var(--color-surface);
82
- --_member-border: var(--color-border-subtle);
83
- --_member-radius: var(--radius-xl);
84
- --_member-padding: var(--space-6);
85
-
86
44
  display: flex;
87
45
  flex-direction: column;
88
- align-items: center;
89
- text-align: center;
90
- gap: var(--space-2);
91
- padding: var(--_member-padding);
92
- background-color: var(--_member-bg);
93
- border: var(--border-width) solid var(--_member-border);
94
- border-radius: var(--_member-radius);
95
- transition:
96
- box-shadow var(--duration-normal) var(--ease-fluid),
97
- transform var(--duration-normal) var(--ease-settle);
46
+ gap: var(--space-xs);
98
47
  }
99
48
 
100
- .team-member:hover {
101
- box-shadow: var(--shadow-md);
102
- transform: translateY(-2px);
103
- }
104
-
105
- .team-member .avatar {
106
- margin-block-end: var(--space-2);
107
- }
49
+ .team-member .avatar { margin-block-end: var(--space-xs); }
108
50
 
109
51
  .team-member__name {
110
- font-size: var(--font-size-lg);
111
- font-weight: var(--weight-semibold, 600);
112
- color: var(--color-text);
52
+ font-family: var(--font-heading);
53
+ font-size: var(--step-1);
54
+ font-weight: calc(500 + var(--_dark-weight));
55
+ line-height: var(--leading-heading);
113
56
  margin: 0;
114
57
  }
115
58
 
116
59
  .team-member__role {
117
- font-size: var(--font-size-sm);
118
- color: var(--accent);
60
+ font-size: var(--step--1);
61
+ color: var(--color-text-muted);
119
62
  margin: 0;
120
63
  }
121
64
 
122
65
  .team-member__bio {
123
- font-size: var(--font-size-sm);
124
66
  color: var(--color-text-muted);
125
- line-height: var(--leading-relaxed);
126
- margin: 0;
127
- margin-block-start: var(--space-2);
67
+ margin: 0; margin-block-start: var(--space-xs);
128
68
  }
129
69
 
130
-
131
- /* ========================================
132
- SOCIAL LINKS
133
- ======================================== */
134
-
135
70
  .team-member__social {
136
71
  display: flex;
137
- gap: var(--space-2);
138
- margin-block-start: var(--space-3);
72
+ gap: var(--space-1);
73
+ margin-block-start: var(--space-xs);
74
+ margin-inline-start: -0.75rem;
139
75
  }
140
76
 
141
77
  .team-member__social a {
142
78
  display: flex;
143
79
  align-items: center;
144
80
  justify-content: center;
145
- inline-size: 2rem;
146
- block-size: 2rem;
147
- border-radius: var(--radius-full);
81
+ inline-size: 2.75rem;
82
+ block-size: 2.75rem;
148
83
  color: var(--color-text-muted);
149
- transition: color var(--duration-fast) var(--ease-fluid);
150
- }
151
-
152
- .team-member__social a:hover {
153
- color: var(--accent);
154
84
  }
155
85
 
156
- .team-member__social a:focus-visible {
157
- outline: 2px solid var(--color-focus);
158
- outline-offset: 2px;
159
- }
86
+ .team-member__social a:hover { color: var(--color-text); }
160
87
 
161
88
  .team-member__social svg {
162
- inline-size: 1rem;
163
- block-size: 1rem;
164
- }
165
-
166
-
167
- /* ========================================
168
- ACCESSIBILITY
169
- ======================================== */
170
-
171
- @media (prefers-reduced-motion: reduce) {
172
- .team-member,
173
- .team-member__social a {
174
- transition: none;
175
- }
176
-
177
- .team-member:hover {
178
- transform: none;
179
- }
89
+ inline-size: 1.125rem;
90
+ block-size: 1.125rem;
180
91
  }
181
92
  }
@@ -48,204 +48,90 @@
48
48
 
49
49
  @layer components {
50
50
 
51
- /* ========================================
52
- BASE TESTIMONIALS SECTION
53
- ======================================== */
54
-
55
- .testimonials {
56
- padding-block: var(--space-16);
57
- padding-inline: var(--space-5);
58
- max-inline-size: var(--container-xl);
59
- margin-inline: auto;
60
- }
61
-
62
- .testimonials__header {
63
- text-align: center;
64
- max-inline-size: var(--container-md);
65
- margin-inline: auto;
66
- margin-block-end: var(--space-12);
67
- }
68
-
69
- .testimonials__header h2 {
70
- font-size: var(--font-size-3xl);
71
- font-weight: var(--weight-bold);
72
- color: var(--color-text);
73
- margin-block-end: var(--space-3);
74
- }
75
-
76
- .testimonials__header p {
77
- font-size: var(--font-size-lg);
78
- color: var(--color-text-muted);
79
- }
51
+ /* The band's padding and edges, and the header (a section head), are in
52
+ src/fluid.css (layouts). */
80
53
 
54
+ .testimonials__header p:not(.eyebrow, .lede) { color: var(--color-text-muted); } /* the label voice owns the eyebrow's colour */
81
55
 
82
- /* ========================================
83
- SINGLE CENTERED (default)
84
- Large featured testimonial
85
- ======================================== */
86
-
56
+ /* A testimonial is a serif quote and a label-voice attribution. No box. */
87
57
  .testimonial {
88
- --_testi-bg: var(--color-surface);
89
- --_testi-border: var(--color-border-subtle);
90
- --_testi-padding: var(--space-8);
91
- --_testi-radius: var(--radius-xl);
92
-
93
58
  display: flex;
94
59
  flex-direction: column;
95
- align-items: center;
96
- gap: var(--space-6);
97
- padding: var(--_testi-padding);
98
- background-color: var(--_testi-bg);
99
- border: var(--border-width) solid var(--_testi-border);
100
- border-radius: var(--_testi-radius);
60
+ gap: var(--space-m);
101
61
  margin: 0;
102
- text-align: center;
103
- position: relative;
104
- }
105
-
106
- /* Decorative quote mark */
107
- .testimonial::before {
108
- content: "\201C";
109
- position: absolute;
110
- inset-block-start: var(--space-4);
111
- inset-inline-start: var(--space-6);
112
- font-size: var(--font-size-5xl);
113
- line-height: 1;
114
- color: color-mix(in oklch, var(--accent) 25%, transparent);
115
- font-family: var(--font-serif, Georgia, serif);
116
- pointer-events: none;
117
62
  }
118
63
 
119
64
  .testimonial__quote {
120
65
  margin: 0;
121
66
  padding: 0;
122
- border: none;
123
- max-inline-size: var(--container-prose);
67
+ border: 0;
68
+ font-style: normal;
124
69
  }
125
70
 
126
71
  .testimonial__quote p {
127
- font-size: var(--font-size-lg);
128
- font-style: italic;
129
- line-height: var(--leading-relaxed);
130
- color: var(--color-text);
72
+ font-family: var(--font-heading);
73
+ font-size: var(--step-2);
74
+ font-weight: calc(var(--weight-light) + var(--_dark-weight));
75
+ line-height: 1.3;
76
+ letter-spacing: var(--tracking-heading);
77
+ text-wrap: balance;
78
+ max-inline-size: 32em;
131
79
  margin: 0;
132
80
  }
133
81
 
82
+ .testimonial__quote p::before { content: "\201C"; }
83
+ .testimonial__quote p::after { content: "\201D"; }
84
+
134
85
  .testimonial__author {
135
86
  display: flex;
136
87
  align-items: center;
137
- gap: var(--space-3);
88
+ gap: var(--space-s);
138
89
  }
139
90
 
140
- .testimonial__author .avatar {
141
- flex-shrink: 0;
142
- }
91
+ .testimonial__author .avatar { flex-shrink: 0; }
143
92
 
144
93
  .testimonial__name {
145
94
  display: block;
146
95
  font-style: normal;
147
- font-weight: var(--weight-semibold, 600);
148
- color: var(--color-text);
149
- font-size: var(--font-size-sm);
96
+ font-weight: 600;
97
+ font-size: var(--step--1);
150
98
  }
151
99
 
152
100
  .testimonial__role {
153
101
  display: block;
154
- font-size: var(--font-size-xs);
102
+ font-size: var(--step--1);
155
103
  color: var(--color-text-muted);
156
104
  }
157
105
 
158
- /* Single centered: wider and more spacious */
159
- .testimonials:not([data-type]) .testimonial {
160
- max-inline-size: var(--container-md);
161
- margin-inline: auto;
162
- padding: var(--space-10);
163
- }
164
-
165
- .testimonials:not([data-type]) .testimonial__quote p {
166
- font-size: var(--font-size-xl);
167
- }
168
-
169
-
170
- /* ========================================
171
- GRID
172
- Multiple testimonials in card grid
173
- ======================================== */
174
-
106
+ /* Grid: smaller quotes under ink hairlines, like cards. */
175
107
  .testimonials__grid {
176
108
  display: grid;
177
- gap: var(--space-6);
178
- grid-template-columns: repeat(
179
- auto-fit,
180
- minmax(min(100%, 18rem), 1fr)
181
- );
109
+ gap: var(--space-l) var(--space-m);
110
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
182
111
  }
183
112
 
184
113
  .testimonials[data-type="grid"] .testimonial {
185
- text-align: start;
186
- align-items: flex-start;
114
+ padding-block-start: var(--space-s);
115
+ border-block-start-color: var(--color-border-strong);
187
116
  }
188
117
 
189
- .testimonials[data-type="grid"] .testimonial::before {
190
- font-size: var(--font-size-4xl);
191
- }
192
-
193
- .testimonials[data-type="grid"] .testimonial__quote p {
194
- font-size: var(--font-size-base);
195
- }
196
-
197
-
198
- /* ========================================
199
- LOGO WALL
200
- Client/partner logos in a responsive row
201
- ======================================== */
118
+ .testimonials[data-type="grid"] .testimonial__quote p { font-size: var(--step-1); }
202
119
 
120
+ /* Logo wall */
203
121
  .testimonials__logos {
204
122
  display: flex;
205
123
  flex-wrap: wrap;
206
- justify-content: center;
207
124
  align-items: center;
208
- gap: var(--space-10);
125
+ gap: var(--space-l);
209
126
  }
210
127
 
211
- .testimonials__logos img,
212
- .testimonials__logos svg {
128
+ .testimonials__logos :is(img, svg) {
213
129
  block-size: 2.5rem;
214
130
  inline-size: auto;
215
131
  object-fit: contain;
216
- opacity: 0.6;
217
- filter: grayscale(100%);
218
- transition:
219
- opacity var(--duration-normal) var(--ease-fluid),
220
- filter var(--duration-normal) var(--ease-fluid);
221
- }
222
-
223
- .testimonials__logos img:hover,
224
- .testimonials__logos svg:hover {
225
- opacity: 1;
226
- filter: grayscale(0%);
227
- }
228
-
229
- .testimonials[data-type="logos"] .testimonials__header {
230
- margin-block-end: var(--space-8);
132
+ opacity: 0.7;
133
+ transition: opacity var(--duration-fast) var(--ease-fluid);
231
134
  }
232
135
 
233
- .testimonials[data-type="logos"] .testimonials__header p {
234
- font-size: var(--font-size-sm);
235
- text-transform: uppercase;
236
- letter-spacing: var(--tracking-wider);
237
- color: var(--color-text-muted);
238
- }
239
-
240
-
241
- /* ========================================
242
- ACCESSIBILITY
243
- ======================================== */
244
-
245
- @media (prefers-reduced-motion: reduce) {
246
- .testimonials__logos img,
247
- .testimonials__logos svg {
248
- transition: none;
249
- }
250
- }
136
+ .testimonials__logos :is(img, svg):hover { opacity: 1; }
251
137
  }
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Fluid Zero - base
3
+ * @layer base
4
+ *
5
+ * Native elements only. Every selector is :where(), so any later rule wins.
6
+ * The reset zeroes margins; the flow below puts them back, once.
7
+ */
8
+
9
+ @layer base {
10
+ :where(:root) {
11
+ accent-color: var(--accent);
12
+ }
13
+
14
+ :where(body) {
15
+ padding-inline: calc(var(--space-gutter) / 2);
16
+ font-family: var(--font-body);
17
+ font-size: clamp(1rem, 0.95rem + 0.25vi, 1.125rem);
18
+ line-height: 1.6;
19
+ color: var(--color-text);
20
+ background-color: var(--color-background);
21
+ }
22
+
23
+ /* Flow: space after each block; more above a heading than below it. */
24
+ :where(p, ul, ol, dl, blockquote, pre, figure, table, hr, form, fieldset, details, address, h1, h2, h3, h4, h5, h6) {
25
+ margin-block: 0 1em;
26
+ }
27
+ :where(* + h1, * + h2, * + h3, * + h4, * + h5, * + h6) {
28
+ margin-block-start: 1.75em;
29
+ }
30
+
31
+ :where(h1, h2, h3, h4, h5, h6) {
32
+ font-family: var(--font-heading);
33
+ font-size: 1em;
34
+ font-weight: 700;
35
+ line-height: 1.15;
36
+ margin-block-end: 0.5em;
37
+ }
38
+ :where(h1) { font-size: clamp(2rem, 1.6667rem + 1.4815vi, 3rem); } /* 32 → 48 */
39
+ :where(h2) { font-size: clamp(1.5rem, 1.3333rem + 0.7407vi, 2rem); } /* 24 → 32 */
40
+ :where(h3) { font-size: clamp(1.25rem, 1.1667rem + 0.3704vi, 1.5rem); } /* 20 → 24 */
41
+
42
+ :where(p, li, dd, blockquote, figcaption) {
43
+ max-inline-size: var(--measure);
44
+ }
45
+ :where(dt) { font-weight: 600; }
46
+ :where(dd) { margin-inline-start: 1.5em; }
47
+
48
+ :where(a) {
49
+ color: var(--accent-ink);
50
+ text-decoration: underline;
51
+ text-decoration-thickness: 0.06em;
52
+ }
53
+ :where(a:hover) { text-decoration-thickness: 0.12em; }
54
+
55
+ :where(blockquote) {
56
+ padding-inline-start: 1em;
57
+ border-inline-start: 3px solid var(--color-border-strong);
58
+ }
59
+
60
+ :where(code, kbd, samp, pre) {
61
+ font-family: var(--font-mono);
62
+ font-size: 0.9em;
63
+ }
64
+ :where(:not(pre) > code, pre) {
65
+ background-color: var(--color-surface);
66
+ }
67
+ :where(:not(pre) > code) { padding: 0.1em 0.3em; }
68
+ :where(pre) {
69
+ padding: 1em;
70
+ overflow-x: auto;
71
+ border: var(--border-width) solid var(--color-border);
72
+ }
73
+
74
+ :where(hr) {
75
+ border: 0;
76
+ border-block-start: var(--border-width) solid var(--color-border);
77
+ margin-block: 2em;
78
+ }
79
+
80
+ :where(figure img) { inline-size: 100%; }
81
+ :where(figcaption) {
82
+ margin-block-start: 0.5em;
83
+ font-size: 0.875em;
84
+ color: var(--color-text-muted);
85
+ }
86
+
87
+ :where(table) { inline-size: 100%; }
88
+ :where(th) { text-align: start; }
89
+ :where(th, td) {
90
+ padding: 0.5em 0.75em 0.5em 0;
91
+ border-block-end: var(--border-width) solid var(--color-border);
92
+ }
93
+
94
+ :where(legend, label) { font-weight: 600; }
95
+
96
+ /* Fields: every text-like input, select and textarea. */
97
+ :where(input:not([type="checkbox"], [type="radio"], [type="range"], [type="color"], [type="file"], [type="submit"], [type="reset"], [type="button"], [type="image"]), select, textarea) {
98
+ min-block-size: 2.75rem;
99
+ padding: 0.5em 0.75em;
100
+ border: var(--border-width) solid var(--color-border-strong);
101
+ border-radius: var(--radius-control);
102
+ background-color: var(--color-background);
103
+ }
104
+ :where(input, select, textarea):focus-visible { outline-offset: 1px; }
105
+ :where(input, select, textarea):user-invalid { border-color: var(--color-error); }
106
+ :where(input:is([type="checkbox"], [type="radio"])) {
107
+ inline-size: 1.25em;
108
+ block-size: 1.25em;
109
+ vertical-align: -0.25em;
110
+ }
111
+
112
+ :where(button, [type="submit"], [type="reset"], [type="button"]) {
113
+ min-block-size: 2.75rem;
114
+ padding: 0.5em 1.25em;
115
+ border: var(--border-width) solid var(--accent);
116
+ border-radius: var(--radius-control);
117
+ background-color: var(--accent);
118
+ color: var(--on-accent);
119
+ font-weight: 600;
120
+ cursor: pointer;
121
+ }
122
+
123
+ :where(:disabled) {
124
+ opacity: 0.5;
125
+ cursor: not-allowed;
126
+ }
127
+
128
+ :where([data-bg="tinted"]) {
129
+ padding: 1.5rem;
130
+ background-color: var(--color-surface);
131
+ }
132
+
133
+ :where(.sr-only) {
134
+ position: absolute;
135
+ inline-size: 1px;
136
+ block-size: 1px;
137
+ overflow: hidden;
138
+ clip-path: inset(50%);
139
+ white-space: nowrap;
140
+ }
141
+ :where(.skip-link) {
142
+ position: absolute;
143
+ inset-block-start: 0.5rem;
144
+ inset-inline-start: 0.5rem;
145
+ padding: 0.5em 1em;
146
+ background-color: var(--color-background);
147
+ translate: 0 -200%;
148
+ }
149
+ :where(.skip-link:focus) { translate: none; }
150
+
151
+ /* Dialog and fieldset: the reset strips their edge, so give it back. */
152
+ :where(dialog, fieldset) {
153
+ border: var(--border-width) solid var(--color-border-strong);
154
+ padding: 1em;
155
+ }
156
+ :where(dialog) {
157
+ color: var(--color-text);
158
+ background-color: var(--color-background);
159
+ }
160
+
161
+ /* No !important: an important rule in this early layer would beat every
162
+ child theme. Browsers drop backgrounds in print on their own. */
163
+ @media print {
164
+ :where(*) { color: #000; }
165
+ }
166
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Fluid Zero - tokens
3
+ * @layer tokens
4
+ *
5
+ * KNOBS share the base's names (set them on :root in @layer themes).
6
+ * ROLES share the base's names too, through light-dark(): the OS decides,
7
+ * <html data-theme="light|dark"> overrides.
8
+ *
9
+ * The plain values first are the floor for browsers without relative colour
10
+ * (Safari before 18); the @supports block derives the same roles from
11
+ * --accent and --neutral-tint, as the base does.
12
+ */
13
+
14
+ @layer tokens {
15
+ :root {
16
+ color-scheme: light dark;
17
+
18
+ /* KNOBS */
19
+ --accent: oklch(42% 0.15 22);
20
+ --neutral-tint: oklch(50% 0.01 75);
21
+ --font-heading: system-ui, sans-serif;
22
+ --font-body: system-ui, sans-serif;
23
+ --font-mono: ui-monospace, monospace;
24
+ --font-serif: ui-serif, Georgia, serif; /* for a site that wants it */
25
+ --measure: 66ch;
26
+ --space-gutter: clamp(2rem, 1rem + 5vi, 5rem); /* both sides together, as in core: 16 → 40px a side */
27
+ --radius-control: 0.25rem;
28
+ --border-width: 1px;
29
+
30
+ /* ROLES: the floor */
31
+ --on-accent: #fff;
32
+ --accent-ink: light-dark(var(--accent), color-mix(in oklch, var(--accent) 40%, #fff));
33
+ --color-text: light-dark(oklch(17% 0.01 75), oklch(93% 0.01 75));
34
+ --color-text-muted: light-dark(oklch(44% 0.01 75), oklch(74% 0.01 75));
35
+ --color-background: light-dark(oklch(99% 0.0075 75), oklch(14.5% 0.01 75));
36
+ --color-surface: light-dark(oklch(95.5% 0.01 75), oklch(19% 0.01 75));
37
+ --color-border: light-dark(oklch(86% 0.01 75), oklch(30% 0.01 75));
38
+ --color-border-strong: var(--color-text);
39
+ --color-error: light-dark(oklch(48% 0.17 25), oklch(76% 0.13 25));
40
+ --color-focus: var(--accent-ink);
41
+ }
42
+
43
+ @supports (color: oklch(from red l c h)) {
44
+ :root {
45
+ --on-accent: oklch(from var(--accent) clamp(0, (0.575 - l) * 1000, 1) 0 0);
46
+ --accent-ink: light-dark(oklch(from var(--accent) min(l, 0.45) c h), oklch(from var(--accent) max(l, 0.76) c h));
47
+ --color-text: light-dark(oklch(from var(--neutral-tint) 0.17 c h), oklch(from var(--neutral-tint) 0.93 c h));
48
+ --color-text-muted: light-dark(oklch(from var(--neutral-tint) 0.44 c h), oklch(from var(--neutral-tint) 0.74 c h));
49
+ --color-background: light-dark(oklch(from var(--neutral-tint) 0.99 calc(c * 0.75) h), oklch(from var(--neutral-tint) 0.145 c h));
50
+ --color-surface: light-dark(oklch(from var(--neutral-tint) 0.955 c h), oklch(from var(--neutral-tint) 0.19 c h));
51
+ --color-border: light-dark(oklch(from var(--neutral-tint) 0.86 c h), oklch(from var(--neutral-tint) 0.30 c h));
52
+ }
53
+ }
54
+
55
+ :root[data-theme="light"] { color-scheme: light; }
56
+ :root[data-theme="dark"] { color-scheme: dark; }
57
+
58
+ @media (prefers-contrast: more) {
59
+ :root {
60
+ --color-text-muted: var(--color-text);
61
+ --color-border: var(--color-border-strong);
62
+ }
63
+ }
64
+ }
package/src/zero.css ADDED
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Fluid Zero: the tiny alternate base (dist/fluid-zero.min.css).
3
+ *
4
+ * A modern cross-browser reset, system fonts, and the least CSS a readable,
5
+ * accessible page needs. For a utility page, an admin screen, or a site that
6
+ * builds its own look from nothing. It is NOT the default base: that is
7
+ * fluid.css.
8
+ *
9
+ * Same layer order and the same knob and role names as the base, so a child
10
+ * theme in @layer themes works on either: the knobs both define take effect
11
+ * on both; component rules simply have nothing to style here.
12
+ * No components, sections, content grid, web fonts or JS.
13
+ * Budget: 4,096 B gzip (build-css.mjs fails above it).
14
+ */
15
+
16
+ @layer reset, tokens, base, layouts, components, themes, utilities;
17
+
18
+ @import "./core/reset.css";
19
+ @import "./zero/tokens.css";
20
+ @import "./zero/base.css";