@stnd/styles 0.5.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.
@@ -0,0 +1,57 @@
1
+ @use "standard-00-variables" as *;
2
+
3
+ /* ===== BULLETPROOF RHYTHM SYSTEM ===== */
4
+ /* Applied to any container — cascades to children via margin-block-start */
5
+
6
+ :where(.rhythm) {
7
+ /* flow-root creates a new Block Formatting Context (BFC).
8
+ This contains child margins — they won't collapse out of .rhythm.
9
+ Think of it as "overflow: hidden" but without clipping content. */
10
+ display: flow-root;
11
+
12
+ :where(:is(#{$rhythm-tags})) + :where(:is(#{$rhythm-tags})) {
13
+ margin-block-start: calc(var(--gap) * 1);
14
+ }
15
+
16
+ > :where(:is(#{$rhythm-double-tags})) + :where(:is(#{$rhythm-simple-tags})),
17
+ > :where(:is(#{$rhythm-simple-tags})) + :where(:is(#{$rhythm-double-tags})) {
18
+ margin-block-start: calc(var(--gap) * var(--gap-multiplicator, 2));
19
+ }
20
+
21
+ // EXCEPTIONS
22
+ //*
23
+ > :where(:is(#{$rhythm-tags})) ~ :where(:is(h1, h2)) {
24
+ margin-block-start: calc(
25
+ var(--gap) * calc(var(--gap-multiplicator, 2) + 3)
26
+ );
27
+ }
28
+
29
+ > :where(:is(#{$rhythm-tags})) ~ :where(:is(h3, h4, h5, h6)) {
30
+ margin-block-start: calc(
31
+ var(--gap) * calc(var(--gap-multiplicator, 2) + 2)
32
+ );
33
+ }
34
+
35
+ > :where(:is(h1, h2, h3, h4)) + :where(:is(h2, h3, h4, h5, h6)) {
36
+ margin-block-start: calc(var(--gap) * var(--gap-multiplicator, 2));
37
+ }
38
+
39
+ > :where(:is(h1, h2, h3)) + * {
40
+ margin-block-start: calc(var(--gap) * var(--gap-multiplicator, 2));
41
+ }
42
+
43
+ > :where(:is(h4, h5, h6)) + * {
44
+ margin-block-start: calc(var(--gap) * 1);
45
+ }
46
+
47
+ //*/
48
+
49
+ .flex > * {
50
+ margin-block-start: 0;
51
+ }
52
+ }
53
+
54
+ /* Individual element overrides */
55
+ .no-rhythm * {
56
+ margin-block-start: 0;
57
+ }
@@ -0,0 +1,289 @@
1
+ @use "standard-00-variables" as *;
2
+
3
+ /**
4
+ * @component Prose Layout System
5
+ * @category Layout
6
+ * @description Prose-focused layout system for optimal reading experience with proper
7
+ * line lengths based on typography research (60-75 characters). Uses CSS Grid with
8
+ * responsive content columns for sidebars, accents, and featured content.
9
+ * Inspired by fine-art typography and classical book design principles.
10
+ *
11
+ * @prop {class} .prose Main article/content container for readable prose
12
+ * @prop {class} .small Narrow sidebar width container
13
+ * @prop {class} .accent Accent container with background color
14
+ * @prop {class} .feature Featured content with larger spacing
15
+ * @prop {class} .hero Full-width container
16
+ * @prop {variable} --line-width-xs Extra small (24rem / 384px)
17
+ * @prop {variable} --line-width-sm Small (32rem / 512px)
18
+ * @prop {variable} --line-width-md Medium (42rem / 672px) - default
19
+ * @prop {variable} --line-width-lg Large (50rem / 800px)
20
+ * @prop {variable} --line-width-xl Extra large (60rem / 960px)
21
+ *
22
+ * @example
23
+ * // Standard article with proper reading width
24
+ * <article class="prose">
25
+ * <h1>Article Title</h1>
26
+ * <p>Content here stays within optimal reading width...</p>
27
+ * </article>
28
+ *
29
+ * // Narrow sidebar content
30
+ * <div class="prose small">
31
+ * <p>Short form content</p>
32
+ * </div>
33
+ *
34
+ * // Featured content with emphasis
35
+ * <section class="prose feature">
36
+ * <blockquote>Important quote with extra space</blockquote>
37
+ * </section>
38
+ *
39
+ * @since 0.1.0
40
+ * @see https://www.rsub.com/blog/how-long-should-a-line-of-text-be/
41
+ */
42
+
43
+ /* Prose Layout Design Tokens - Using logical properties */
44
+
45
+ /* Prose Layout System - Article content grid and positioning */
46
+ /* Inspired by fine-art typography and book design principles */
47
+
48
+ :where(.prose) {
49
+ display: grid;
50
+ align-content: start;
51
+
52
+ /* Left-aligned variant — content hugs the left edge */
53
+ &.left {
54
+ margin-inline: 0;
55
+ grid-template-columns:
56
+ [hero-start feature-start editorial-start content-start] var(
57
+ --content-width
58
+ )
59
+ [content-end editorial-end feature-end] 1fr [hero-end];
60
+ }
61
+
62
+ grid-template-columns:
63
+ [hero-start] var(--content-width-hero)
64
+ [feature-start] var(--content-width-feature) [editorial-start] var(
65
+ --content-width-editorial
66
+ )
67
+ [content-start] var(--content-width) [content-end] var(
68
+ --content-width-editorial
69
+ )
70
+ [editorial-end] var(--content-width-feature) [feature-end] var(
71
+ --content-width-hero
72
+ )
73
+ [hero-end];
74
+
75
+ transition: grid-template-columns var(--transition);
76
+
77
+ position: relative;
78
+ max-width: var(--prose-max-width, var(--max-width));
79
+ /*z-index: var(--z-base);*/
80
+ /* Default content positioning */
81
+
82
+ > * {
83
+ grid-column: content;
84
+ min-width: 0;
85
+ /* Prevents grid blowout from code blocks and tables */
86
+ transition:
87
+ margin-inline var(--transition),
88
+ padding-inline var(--transition);
89
+ }
90
+
91
+ > .small,
92
+ > .container-small,
93
+ > .callout,
94
+ > blockquote,
95
+ > pre,
96
+ > figure,
97
+ > p:has(img),
98
+ > .stnd-code-block {
99
+ grid-column: content;
100
+ margin-inline: var(--content-width-sm);
101
+ }
102
+
103
+ table,
104
+ .scroll:has(table),
105
+ .editorial {
106
+ grid-column: editorial;
107
+ margin-inline: 0;
108
+ max-width: none;
109
+ }
110
+
111
+ .feature {
112
+ grid-column: feature;
113
+ margin-inline: 0;
114
+ max-width: none;
115
+ }
116
+
117
+ .full,
118
+ .hero {
119
+ grid-column: hero;
120
+ max-width: 100vw;
121
+ }
122
+
123
+ .hero {
124
+ margin-inline: calc(var(--gap-body) * -1);
125
+ border-radius: 0 !important;
126
+
127
+ > * {
128
+ margin-inline: 0;
129
+ }
130
+ }
131
+
132
+ /* =========================== */
133
+ /* TYPOGRAPHIC NUANCES */
134
+ /* =========================== */
135
+ .video-container {
136
+ position: relative;
137
+ padding-bottom: 56.25%;
138
+ /* 16:9 aspect ratio */
139
+ height: 0;
140
+ overflow: hidden;
141
+ border-radius: var(--radius);
142
+ box-shadow: var(--shadow-raised);
143
+
144
+ iframe {
145
+ position: absolute;
146
+ top: 0;
147
+ left: 0;
148
+ width: 100%;
149
+ height: 100%;
150
+ border: 0;
151
+ }
152
+ }
153
+
154
+ /* Image gallery layout */
155
+ &:not(.no-grid) {
156
+ p:has(img),
157
+ p:has(video) {
158
+ display: flex;
159
+ gap: var(--gap-grid-img);
160
+ border-radius: var(--radius);
161
+
162
+ > * {
163
+ flex: 1 1 auto;
164
+ min-width: 1%;
165
+ min-height: 1%;
166
+ object-fit: cover;
167
+ }
168
+
169
+ img,
170
+ img:first-child,
171
+ img:last-child {
172
+ margin-inline: auto;
173
+ flex-grow: 0;
174
+ /* prevents the image from stretching to fill */
175
+ }
176
+
177
+ br {
178
+ display: none;
179
+ }
180
+ }
181
+
182
+ .callout p:has(img),
183
+ .callout p:has(img) br {
184
+ display: block;
185
+ }
186
+
187
+ /* Adjacent images get reduced spacing + var(--img-gap) */
188
+ p:has(img) + p:has(img) {
189
+ margin-block-start: var(--gap-grid-img) !important;
190
+ }
191
+ }
192
+ /* Left-aligned variant — content hugs the left edge */
193
+ &:not(.no-grid).left p:has(img) img,
194
+ &:not(.no-grid).left p:has(img) img:first-child,
195
+ &:not(.no-grid).left p:has(img) img:last-child {
196
+ margin-inline: 0 !important;
197
+ }
198
+
199
+ /* Note: Page's main title h1:first-child retains spacing via direct child selector */
200
+ /* The > h1:first-child selector in h1 rule ensures it overrides rhythm's > * margin-reset */
201
+
202
+ /* Basic media styling */
203
+ img,
204
+ audio,
205
+ video,
206
+ iframe {
207
+ margin: 0;
208
+ margin-inline: auto;
209
+ max-inline-size: 100%;
210
+ }
211
+
212
+ p {
213
+ text-wrap: pretty;
214
+ overflow-wrap: anywhere;
215
+ }
216
+
217
+ h1,
218
+ h2,
219
+ h3,
220
+ h4,
221
+ h5,
222
+ h6 {
223
+ text-wrap: balance;
224
+ }
225
+
226
+ @media (max-width: #{$mobile}) {
227
+ .container-small,
228
+ .callout,
229
+ blockquote,
230
+ pre,
231
+ figure,
232
+ p:has(img),
233
+ .stnd-code-block {
234
+ margin-inline: 0 !important;
235
+ }
236
+ }
237
+
238
+ @media (max-width: #{$small}) {
239
+ .card.accent,
240
+ .card.feature,
241
+ .card.editorial,
242
+ .card.full {
243
+ margin-inline: calc(var(--space) * -1);
244
+ border-radius: 0;
245
+ }
246
+ }
247
+
248
+ /* Mermaid diagrams */
249
+ .mermaid {
250
+ margin-block-end: var(--space);
251
+ margin-inline: auto;
252
+ padding: var(--space);
253
+ }
254
+
255
+ p img {
256
+ max-inline-size: 100%;
257
+ }
258
+
259
+ a {
260
+ overflow-wrap: break-word;
261
+ }
262
+
263
+ .mycelial-graft {
264
+ text-decoration: none;
265
+ border-bottom: 1px dotted var(--color-text-muted);
266
+ color: inherit;
267
+ cursor: help;
268
+ transition: all 0.2s ease;
269
+
270
+ &:hover {
271
+ color: var(--color-primary);
272
+ border-bottom: 1px solid var(--color-primary);
273
+ background-color: var(--color-surface-hover);
274
+ }
275
+ }
276
+ }
277
+
278
+ /* Animation keyframes */
279
+ @keyframes fadeInUp {
280
+ from {
281
+ opacity: 0;
282
+ transform: translateY(0.5rem);
283
+ }
284
+
285
+ to {
286
+ opacity: 1;
287
+ transform: translateY(0);
288
+ }
289
+ }
@@ -0,0 +1,216 @@
1
+ @use "standard-00-variables" as *;
2
+
3
+ /* =========================== */
4
+ /* BASE (HTML ROOT) */
5
+ /* =========================== */
6
+
7
+ html {
8
+ /* Use the explicit background-color longhand so transitions target
9
+ the color property directly (avoids shorthand incompatibilities).
10
+ Keep font-smoothing here for consistent rendering across color modes. */
11
+ background-color: var(--color-background);
12
+ color: var(--color-foreground);
13
+
14
+ scroll-behavior: smooth;
15
+ -webkit-overflow-scrolling: touch;
16
+
17
+ font-family: var(--font-text), system-ui, sans-serif;
18
+ font-size: var(--size-base);
19
+ font-weight: var(--font-weight);
20
+ letter-spacing: var(--font-letter-spacing);
21
+
22
+ line-height: var(--line-height);
23
+
24
+ text-rendering: optimizeLegibility;
25
+ /* kerning & ligatures */
26
+ -webkit-font-smoothing: antialiased;
27
+ -moz-osx-font-smoothing: grayscale;
28
+
29
+ font-optical-sizing: auto;
30
+ font-feature-settings: var(--font-feature);
31
+ font-variation-settings: var(--font-variation);
32
+
33
+ text-wrap: pretty;
34
+ hyphens: none;
35
+ widows: 2;
36
+ orphans: 2;
37
+ }
38
+
39
+ @media (prefers-reduced-motion: no-preference) {
40
+ /* Smoothly transition global surfaces and text color together.
41
+ Add `will-change` to hint the UA for smoother repainting on theme toggles.
42
+ Keep motion-reduced users in mind via media query above.
43
+ */
44
+ html,
45
+ body,
46
+ [data-theme] {
47
+ transition:
48
+ background-color var(--transition),
49
+ color var(--transition),
50
+ border-color var(--transition);
51
+ will-change: background-color, color;
52
+ /* Ensure consistent font smoothing during theme switches */
53
+ -webkit-font-smoothing: antialiased;
54
+ -moz-osx-font-smoothing: grayscale;
55
+ }
56
+
57
+ a {
58
+ transition: color var(--transition-fast);
59
+ }
60
+ }
61
+
62
+ body {
63
+ margin: 0;
64
+ padding: var(--gap-body);
65
+ padding-top: var(--gap-header);
66
+ padding-bottom: var(--gap-footer);
67
+ max-width: var(--body-max-width, var(--max-width));
68
+ margin-inline: auto;
69
+
70
+ transition:
71
+ background-color var(--transition),
72
+ color var(--transition),
73
+ border-color var(--transition),
74
+ padding var(--transition),
75
+ margin var(--transition);
76
+
77
+ [id] {
78
+ scroll-margin-top: calc(var(--space-2) + var(--gap-header));
79
+ /* Adjust value as needed background: var(--color-surface-highest); background: var(--color-surface-highest); background: var(--color-surface-highest); background: var(--color-surface-highest); */
80
+ }
81
+ }
82
+
83
+ main {
84
+ transition:
85
+ background-color var(--transition),
86
+ color var(--transition),
87
+ border-color var(--transition),
88
+ box-shadow var(--transition),
89
+ padding var(--transition),
90
+ margin var(--transition);
91
+ }
92
+
93
+ /* Selection highlight */
94
+ ::selection {
95
+ background: oklch(from var(--color-yellow) l c h / 0.15);
96
+ color: var(--color-accent);
97
+ }
98
+
99
+ /* =========================== */
100
+ /* TEXT LINE WIDTH */
101
+ /* =========================== */
102
+ /* Comfortable reading length for text elements — works everywhere,
103
+ not just inside .prose. Images, tables, code blocks are not affected. */
104
+
105
+ p,
106
+ h1,
107
+ h2,
108
+ h3,
109
+ h4,
110
+ h5,
111
+ h6,
112
+ li,
113
+ div.callout,
114
+ summary {
115
+ max-width: var(--line-width);
116
+ }
117
+ p:has(img),
118
+ blockquote p {
119
+ max-width: unset;
120
+ }
121
+
122
+ /* Horizontal rules */
123
+ hr {
124
+ height: var(--stroke-width, 1px);
125
+ background: var(--color-border);
126
+ border: none;
127
+ block-size: var(--stroke-width, 1px);
128
+ }
129
+
130
+ /* Mark highlighting */
131
+ mark {
132
+ background: oklch(from var(--color-yellow) l c h / 0.3);
133
+ color: color-mix(in oklch, var(--color-yellow) 30%, var(--color-foreground));
134
+ padding: var(--trim);
135
+ padding-top: 0.1em;
136
+ }
137
+
138
+ /* =========================== */
139
+ /* LAYOUT */
140
+ /* =========================== */
141
+
142
+ section.container-hero {
143
+ padding-block: var(--space-2);
144
+ border-top: var(--border);
145
+ border-bottom: var(--border);
146
+ box-shadow: var(--shadow);
147
+
148
+ p {
149
+ max-width: var(--line-width);
150
+ margin-inline: auto !important;
151
+ }
152
+ }
153
+
154
+ dt {
155
+ color: var(--color-header);
156
+ font-weight: var(--font-weight-bold);
157
+
158
+ & + dd {
159
+ margin-block-start: var(--space-half);
160
+ line-height: var(--line-height-compact);
161
+ }
162
+ }
163
+
164
+ /* =========================== */
165
+ /* BODY PADDING NEGATION */
166
+ /* =========================== */
167
+
168
+ /**
169
+ * Utilities to negate body padding for full-width elements
170
+ * Useful for footers, headers, or other elements that should extend to viewport edges
171
+ *
172
+ * .negate-body-padding — Negates horizontal body padding only
173
+ * .negate-body-padding-full — Negates all body padding (horizontal + bottom)
174
+ * .negate-body-padding-bottom — Negates extra bottom body padding (1.5× multiplier)
175
+ *
176
+ * Usage:
177
+ * <footer class="negate-body-padding-full">
178
+ * Full-width footer content
179
+ * </footer>
180
+ */
181
+
182
+ /**
183
+ * Negate horizontal body padding (left/right)
184
+ * Allows full-width backgrounds while keeping vertical padding
185
+ */
186
+
187
+ body > footer.no-body-margin:last-of-type,
188
+ body > header.no-body-margin:first-of-type {
189
+ margin-inline: calc(var(--gap-body) * -1);
190
+ padding-inline: calc(var(--gap-body) * 1);
191
+
192
+ @media (max-width: #{$small}) {
193
+ margin-inline: calc(var(--gap-body-mobile) * -1);
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Negate bottom body padding (extra 1.5× multiplier)
199
+ * Removes the generous bottom padding applied to body elements
200
+ * Useful when you want footer to sit flush at bottom
201
+ */
202
+ body > footer.no-body-margin:last-of-type {
203
+ margin-block-end: calc(var(--gap-body) * -1.5);
204
+
205
+ @media (max-width: #{$small}) {
206
+ margin-block-end: calc(var(--gap-body-mobile) * -1.5);
207
+ }
208
+ }
209
+
210
+ body > header.no-body-margin:first-of-type {
211
+ margin-block-start: calc(var(--gap-body) * -1);
212
+
213
+ @media (max-width: #{$small}) {
214
+ margin-block-end: calc(var(--gap-body-mobile) * -1);
215
+ }
216
+ }