@refrakt-md/skeleton 0.25.0 → 0.26.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.
package/index.css CHANGED
@@ -36,6 +36,7 @@
36
36
  @import './styles/dimensions/cover.css' layer(skeleton);
37
37
  @import './styles/dimensions/metadata.css' layer(skeleton);
38
38
  @import './styles/dimensions/sections.css' layer(skeleton);
39
+ @import './styles/dimensions/reading.css' layer(skeleton);
39
40
  @import './styles/dimensions/checklist.css' layer(skeleton);
40
41
  @import './styles/dimensions/sequence.css' layer(skeleton);
41
42
  @import './styles/dimensions/guest-posture.css' layer(skeleton);
@@ -84,6 +85,7 @@
84
85
  @import './styles/runes/progress.css' layer(skeleton);
85
86
  @import './styles/runes/diagram.css' layer(skeleton);
86
87
  @import './styles/runes/gallery.css' layer(skeleton);
88
+ @import './styles/runes/carousel.css' layer(skeleton);
87
89
  @import './styles/runes/diff.css' layer(skeleton);
88
90
  @import './styles/runes/chart.css' layer(skeleton);
89
91
  @import './styles/runes/juxtapose.css' layer(skeleton);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@refrakt-md/skeleton",
3
3
  "description": "The structural cascade layer + layer-order contract every refrakt theme builds on — ships `@layer skeleton` and the `@layer skeleton, skin;` order declaration, plus the token-name contract",
4
- "version": "0.25.0",
4
+ "version": "0.26.0",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -29,7 +29,7 @@
29
29
  "styles"
30
30
  ],
31
31
  "dependencies": {
32
- "@refrakt-md/types": "0.25.0"
32
+ "@refrakt-md/types": "0.26.0"
33
33
  },
34
34
  "scripts": {
35
35
  "build": "tsc"
@@ -0,0 +1,18 @@
1
+ /* Reading register (SPEC-108) — structural treatment of body text, keyed on the
2
+ * `data-reading` refinement of `data-section="body"`. `prose` caps the line-length
3
+ * measure (distinct from the block's `width`) and sets running-text niceties; the
4
+ * default `ui` is unstyled here. The skin (Lumina) owns type, rhythm, and the
5
+ * drop-cap glyph. */
6
+ [data-reading="prose"] {
7
+ max-width: var(--rf-measure, 66ch);
8
+ text-wrap: pretty;
9
+ }
10
+ [data-reading="prose"] > p {
11
+ hanging-punctuation: first last;
12
+ }
13
+
14
+ /* Drop cap — float the opening letter of a prose body. Gated on the prose register
15
+ * (the engine only emits `data-dropcap` there). */
16
+ [data-reading="prose"][data-dropcap] > p:first-child::first-letter {
17
+ float: left;
18
+ }
@@ -80,8 +80,17 @@ html:has(.rf-docs-header) { scroll-padding-top: 5rem; }
80
80
  @media (max-width: 768px) {
81
81
  .rf-docs-header { position: sticky; top: 0; }
82
82
  .rf-docs-header__inner p ~ p { display: none; }
83
- .rf-docs-toolbar { display: flex; position: sticky; top: var(--rf-header-height, 3.5rem); z-index: 100; }
84
- /* header height + ~2.75rem toolbar */
85
- .rf-mobile-panel--nav { top: calc(var(--rf-header-height, 3.5rem) + 2.75rem); }
83
+ /* Pin the toolbar to a known height (border-box, so padding + border are
84
+ included) so the docked nav panel can offset against it exactly. A
85
+ content-driven height let the panel's hardcoded guess undershoot the real
86
+ toolbar, and the toolbar's higher z-index then covered the panel's first
87
+ link. */
88
+ .rf-docs-toolbar { display: flex; position: sticky; top: var(--rf-header-height, 3.5rem); z-index: 100; height: var(--rf-docs-toolbar-height, 3.25rem); box-sizing: border-box; }
89
+ /* Two classes (0,2,0) so this beats the base `.rf-mobile-panel { top }` rule
90
+ in mobile.css — both live in @layer skeleton and mobile.css is imported
91
+ later, so an equal-specificity single-class override would lose on source
92
+ order and the panel would dock under the site header (hiding its first
93
+ link behind the toolbar) instead of under the toolbar. */
94
+ .rf-mobile-panel.rf-mobile-panel--nav { top: calc(var(--rf-header-height, 3.5rem) + var(--rf-docs-toolbar-height, 3.25rem)); }
86
95
  .rf-docs-sidebar { display: none; }
87
96
  }
@@ -117,7 +117,16 @@
117
117
  }
118
118
 
119
119
  /* ─── Collapse breakpoints — beside layouts drop to one column ─── */
120
- @media (max-width: 640px) {
120
+ /* Container-query driven (WORK-490): the collapse responds to the rune's
121
+ * available width — its nearest query container — rather than the global
122
+ * viewport. That container is `.rf-page-content` on a page (content runes always
123
+ * render inside it; it is `container-type: inline-size`) and the `preview`
124
+ * viewport frame inside a preview, so a beside layout in a narrow column collapses
125
+ * when *that column* is tight, and the flip is faithfully simulable in the
126
+ * preview's viewport selector. The rule structure is unchanged from the prior
127
+ * `@media` form — only the query source moves from viewport to container. Shares
128
+ * the `data-collapse` hook (default sm/640px; `never` opts out). */
129
+ @container (max-width: 640px) {
121
130
  :is([data-media-position="start"], [data-media-position="end"])[data-collapse="sm"],
122
131
  :is([data-media-position="start"], [data-media-position="end"]):not([data-collapse]) {
123
132
  grid-template-columns: 1fr;
@@ -128,7 +137,7 @@
128
137
  grid-row: auto;
129
138
  }
130
139
  }
131
- @media (max-width: 768px) {
140
+ @container (max-width: 768px) {
132
141
  :is([data-media-position="start"], [data-media-position="end"])[data-collapse="md"] {
133
142
  grid-template-columns: 1fr;
134
143
  }
@@ -137,7 +146,7 @@
137
146
  grid-row: auto;
138
147
  }
139
148
  }
140
- @media (max-width: 1024px) {
149
+ @container (max-width: 1024px) {
141
150
  :is([data-media-position="start"], [data-media-position="end"])[data-collapse="lg"] {
142
151
  grid-template-columns: 1fr;
143
152
  }
@@ -0,0 +1,98 @@
1
+ /* carousel — shared layout-mode structure (SPEC-100). Any rune that emits the
2
+ * carousel contract — host `[data-layout="carousel"]`, track `[data-name="items"]`,
3
+ * items `[data-name="item"]` — gets the scroll-snap track and nav positioning,
4
+ * with zero per-rune CSS. Nav chrome (colour/shape) is skin (Lumina's
5
+ * styles/runes/carousel.css). The block-agnostic behavior injects the
6
+ * `.rf-carousel__nav` buttons. */
7
+ [data-layout="carousel"] [data-name="items"] {
8
+ display: flex;
9
+ overflow-x: auto;
10
+ scroll-snap-type: x mandatory;
11
+ -webkit-overflow-scrolling: touch;
12
+ }
13
+ /* Slides are the track's direct children (whatever the rune names them). A rune
14
+ * may override the per-slide width via `--rf-carousel-slide`. */
15
+ [data-layout="carousel"] [data-name="items"] > * {
16
+ scroll-snap-align: start;
17
+ flex: 0 0 var(--rf-carousel-slide, 80%);
18
+ }
19
+
20
+ /* Collapse-to-carousel (SPEC-100, WORK-489): a rune whose collapsed form is
21
+ * `carousel` (`collapse-to="carousel"`) flips its item band from grid/list to a
22
+ * scroll-snap row below the `collapse` breakpoint.
23
+ *
24
+ * Container-query driven: the host is its own query container, so the breakpoint
25
+ * is measured against the rune's OWN width — the space it actually occupies —
26
+ * not the global viewport. A `feature` bled to full width collapses on its full
27
+ * width; one sitting in a narrow column collapses on that column. As a bonus this
28
+ * makes the flip faithful inside the `preview` rune's viewport selector: the
29
+ * previewed host fills the constrained frame, so narrowing the frame shrinks the
30
+ * container and fires the breakpoint with no per-preview wiring.
31
+ *
32
+ * CSS-only — no JS nav on the responsive path — and shares the `data-collapse`
33
+ * hook (default sm/640px; `never` opts out). The `container-type` lands only on
34
+ * hosts that opted into `collapse-to="carousel"`, so the blast radius is narrow. */
35
+ [data-collapse-to="carousel"] {
36
+ container-type: inline-size;
37
+ }
38
+ @container (max-width: 640px) {
39
+ [data-collapse-to="carousel"][data-collapse="sm"] [data-name="items"],
40
+ [data-collapse-to="carousel"]:not([data-collapse]) [data-name="items"] {
41
+ display: flex;
42
+ overflow-x: auto;
43
+ scroll-snap-type: x mandatory;
44
+ -webkit-overflow-scrolling: touch;
45
+ grid-template-columns: none;
46
+ }
47
+ [data-collapse-to="carousel"][data-collapse="sm"] [data-name="items"] > *,
48
+ [data-collapse-to="carousel"]:not([data-collapse]) [data-name="items"] > * {
49
+ scroll-snap-align: start;
50
+ flex: 0 0 var(--rf-carousel-slide, 80%);
51
+ }
52
+ }
53
+ @container (max-width: 768px) {
54
+ [data-collapse-to="carousel"][data-collapse="md"] [data-name="items"] {
55
+ display: flex;
56
+ overflow-x: auto;
57
+ scroll-snap-type: x mandatory;
58
+ -webkit-overflow-scrolling: touch;
59
+ grid-template-columns: none;
60
+ }
61
+ [data-collapse-to="carousel"][data-collapse="md"] [data-name="items"] > * {
62
+ scroll-snap-align: start;
63
+ flex: 0 0 var(--rf-carousel-slide, 80%);
64
+ }
65
+ }
66
+ @container (max-width: 1024px) {
67
+ [data-collapse-to="carousel"][data-collapse="lg"] [data-name="items"] {
68
+ display: flex;
69
+ overflow-x: auto;
70
+ scroll-snap-type: x mandatory;
71
+ -webkit-overflow-scrolling: touch;
72
+ grid-template-columns: none;
73
+ }
74
+ [data-collapse-to="carousel"][data-collapse="lg"] [data-name="items"] > * {
75
+ scroll-snap-align: start;
76
+ flex: 0 0 var(--rf-carousel-slide, 80%);
77
+ }
78
+ }
79
+
80
+ /* Nav buttons — positioned relative to the track's container (the behavior
81
+ * ensures it is a positioning context). */
82
+ .rf-carousel__nav {
83
+ position: absolute;
84
+ top: 50%;
85
+ transform: translateY(-50%);
86
+ z-index: 2;
87
+ width: 2.5rem;
88
+ height: 2.5rem;
89
+ display: flex;
90
+ align-items: center;
91
+ justify-content: center;
92
+ }
93
+ .rf-carousel__nav--prev {
94
+ left: 0.5rem;
95
+ }
96
+ .rf-carousel__nav--next {
97
+ right: 0.5rem;
98
+ }
@@ -1,15 +1,21 @@
1
1
  /* cast — structure. The members list reset, grid/list arrangements, name/role
2
2
  * block display, empty-body hide, the avatar box (size/crop/display), and the list
3
3
  * variant flex. Surface chrome, type, radii, and margins are skin. */
4
- .rf-cast__members { list-style: none; }
5
- .rf-cast--grid .rf-cast__members {
4
+ .rf-cast__items { list-style: none; }
5
+ .rf-cast--grid .rf-cast__items {
6
6
  display: grid;
7
7
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
8
8
  }
9
- .rf-cast--list .rf-cast__members {
9
+ .rf-cast--list .rf-cast__items {
10
10
  display: flex;
11
11
  flex-direction: column;
12
12
  }
13
+ /* SPEC-100: carousel arrangement reuses the shared track (styles/runes/carousel.css,
14
+ * keyed on [data-layout="carousel"] [data-name="items"]); cast only sets its
15
+ * per-slide width. */
16
+ .rf-cast--carousel {
17
+ --rf-carousel-slide: clamp(200px, 40%, 280px);
18
+ }
13
19
  .rf-cast-member__name { display: block; }
14
20
  .rf-cast-member__role { display: block; }
15
21
  .rf-cast-member__body:empty { display: none; }
@@ -22,14 +22,36 @@
22
22
  display: contents;
23
23
  }
24
24
 
25
- /* Definitions stack by default; the grid modifier tiles them. */
26
- .rf-feature__definitions {
25
+ /* Item arrangement is the `layout` axis (SPEC-099): `list` stacks the
26
+ * definitions in a single column, `grid` tiles them. Keyed off `[data-layout]`,
27
+ * decoupled from media placement. */
28
+ .rf-feature[data-layout="list"] .rf-feature__items {
27
29
  display: block;
28
30
  }
29
- .rf-feature--definitions-grid .rf-feature__definitions {
31
+ .rf-feature[data-layout="grid"] .rf-feature__items {
30
32
  display: grid;
31
33
  grid-template-columns: repeat(auto-fit, minmax(max(240px, calc((100% - 3rem) / 3)), 1fr));
32
34
  }
35
+ /* Collapse-to-column (SPEC-099 §3): below the `collapse` breakpoint a `grid`
36
+ * arrangement reflows to a single column. CSS-only, sharing the media-split
37
+ * breakpoint hook (`data-collapse`; default sm/640px, mirroring split.css).
38
+ * `collapse="never"` opts out. */
39
+ @media (max-width: 640px) {
40
+ .rf-feature[data-layout="grid"][data-collapse="sm"] .rf-feature__items,
41
+ .rf-feature[data-layout="grid"]:not([data-collapse]) .rf-feature__items {
42
+ grid-template-columns: 1fr;
43
+ }
44
+ }
45
+ @media (max-width: 768px) {
46
+ .rf-feature[data-layout="grid"][data-collapse="md"] .rf-feature__items {
47
+ grid-template-columns: 1fr;
48
+ }
49
+ }
50
+ @media (max-width: 1024px) {
51
+ .rf-feature[data-layout="grid"][data-collapse="lg"] .rf-feature__items {
52
+ grid-template-columns: 1fr;
53
+ }
54
+ }
33
55
 
34
56
  /* Beside layouts: definitions stay stacked inside the content track. */
35
57
  .rf-feature[data-media-position="start"] .rf-feature__content,
@@ -25,35 +25,11 @@
25
25
  right: 0;
26
26
  }
27
27
 
28
- /* Carousel */
29
- .rf-gallery[data-layout="carousel"] .rf-gallery__items {
30
- display: flex;
31
- overflow-x: auto;
32
- scroll-snap-type: x mandatory;
33
- -webkit-overflow-scrolling: touch;
34
- }
28
+ /* Carousel — the scroll-snap track mechanics and the nav buttons are the shared
29
+ * carousel layout mode (styles/runes/carousel.css, keyed on the contract);
30
+ * gallery only sets its column-based item width. */
35
31
  .rf-gallery[data-layout="carousel"] .rf-gallery__item {
36
32
  flex: 0 0 calc(100% / var(--gallery-columns, 3));
37
- scroll-snap-align: start;
38
- }
39
-
40
- /* Carousel nav buttons */
41
- .rf-gallery__nav {
42
- position: absolute;
43
- top: 50%;
44
- transform: translateY(-50%);
45
- z-index: 2;
46
- width: 2.5rem;
47
- height: 2.5rem;
48
- display: flex;
49
- align-items: center;
50
- justify-content: center;
51
- }
52
- .rf-gallery__nav--prev {
53
- left: calc(var(--rf-gallery-edge) + 0.5rem);
54
- }
55
- .rf-gallery__nav--next {
56
- right: calc(var(--rf-gallery-edge) + 0.5rem);
57
33
  }
58
34
 
59
35
  /* Masonry */
@@ -8,10 +8,20 @@
8
8
  justify-content: space-between;
9
9
  min-height: 2rem;
10
10
  }
11
+ .rf-preview__toolbar-left {
12
+ display: flex;
13
+ align-items: center;
14
+ }
11
15
  .rf-preview__controls {
12
16
  display: flex;
13
17
  align-items: center;
14
18
  }
19
+ /* Preview-only controls are hidden in code view (the canvas they tune isn't
20
+ * shown). The explicit display:flex above would defeat the [hidden] attribute,
21
+ * so restore display:none for the hidden state. */
22
+ .rf-preview__controls[hidden] {
23
+ display: none;
24
+ }
15
25
  .rf-preview__view-toggle { display: flex; }
16
26
  .rf-preview__toggle { display: flex; }
17
27
  .rf-preview__toggle-btn {
@@ -22,10 +32,21 @@
22
32
  height: 1.625rem;
23
33
  }
24
34
  .rf-preview__canvas { overflow: hidden; }
35
+ /* Clip the source panel to its rounded corners (matching the canvas) — otherwise
36
+ * the code block's square fill bleeds past the rounded border at the bottom. */
37
+ .rf-preview__source { overflow: hidden; }
25
38
  .rf-preview__viewport-toggle { display: flex; }
26
39
  .rf-preview__viewport-frame {
27
40
  position: relative;
28
41
  overflow: hidden;
42
+ /* The frame is a query container (WORK-490): container-query-driven layouts
43
+ * inside the preview (e.g. split.css beside→stack, carousel collapse-to)
44
+ * resolve against the constrained frame width, so the viewport selector
45
+ * faithfully drives their breakpoints. Runes that establish a closer container
46
+ * (carousel hosts) keep querying themselves; this only governs content with no
47
+ * nearer container. The preview's own toolbar/canvas chrome sits outside the
48
+ * frame, so its @container rules are unaffected. */
49
+ container-type: inline-size;
29
50
  }
30
51
  .rf-preview__viewport-frame--constrained {
31
52
  margin: 0 auto;
@@ -48,7 +48,10 @@
48
48
  .rf-step h4,
49
49
  .rf-step h5,
50
50
  .rf-step h6,
51
- .rf-step strong {
51
+ /* A leading bold opening the step body acts as its title (list-based steps),
52
+ * so it blocks onto its own line. Scoped to the first inline `strong` of the
53
+ * first paragraph — inline bold elsewhere in the body must stay inline. */
54
+ .rf-step__content > p:first-child > strong:first-child {
52
55
  display: block;
53
56
  }
54
57
 
@@ -1,12 +1,9 @@
1
- /* textblock — structure. The body block, the drop-cap float, and the multi-column
2
- * counts (+ responsive). The drop-cap/lead type, column gap/rule, and text-align
3
- * are skin. */
1
+ /* textblock — structure. The body block and the multi-column counts (+
2
+ * responsive). Drop-cap is the universal SPEC-108 opt-in (dimensions/reading.css);
3
+ * lead type, column gap/rule, and text-align are skin. */
4
4
  .rf-textblock__body {
5
5
  display: block;
6
6
  }
7
- .rf-textblock--dropcap .rf-textblock__body > p:first-child::first-letter {
8
- float: left;
9
- }
10
7
  .rf-textblock[data-columns="2"] .rf-textblock__body { column-count: 2; }
11
8
  .rf-textblock[data-columns="3"] .rf-textblock__body { column-count: 3; }
12
9
  .rf-textblock[data-columns="4"] .rf-textblock__body { column-count: 4; }