@skra/ui 0.8.0 → 0.9.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/consumer-guide/README.md +1 -1
- package/consumer-guide/layout.md +116 -38
- package/fesm2022/skra-ui-layout.mjs +74 -8
- package/fesm2022/skra-ui-layout.mjs.map +1 -1
- package/fesm2022/skra-ui-link.mjs +62 -2
- package/fesm2022/skra-ui-link.mjs.map +1 -1
- package/package.json +2 -2
- package/types/skra-ui-layout.d.ts +31 -2
- package/types/skra-ui-layout.d.ts.map +1 -1
- package/types/skra-ui-link.d.ts +28 -1
- package/types/skra-ui-link.d.ts.map +1 -1
package/consumer-guide/README.md
CHANGED
|
@@ -60,7 +60,7 @@ Violating these is a review blocker in a consumer repo just as inside Skra.
|
|
|
60
60
|
| **File upload** | `file-upload.md` | Choosing files (picker / drop zone), a file list with progress, the queue model, a tus upload; `accept` and language limits | ✅ draft |
|
|
61
61
|
| **Feedback** | `feedback.md` | Alert vs toast vs badge vs field error; `live` announcing; dismissal; an alert with a next-step action | ✅ draft |
|
|
62
62
|
| **Overlays** | `overlays.md` | Dialog / tooltip / popover / menu / select in the top layer; shadow & iframe limits | ✅ draft |
|
|
63
|
-
| **Layout** | `layout.md` | Spacing & composition with Stack / Box / Surface / Grid;
|
|
63
|
+
| **Layout** | `layout.md` | Spacing & composition with Stack / Box / Surface / Grid; cards, nav links, the gap scale | ✅ draft |
|
|
64
64
|
| **Wrapping Skra** | `wrapping-patterns.md` | Building your own higher-level components over Skra atoms (simplified API) | ✅ draft |
|
|
65
65
|
| **Custom components** | `custom-components.md` | Authoring a net-new widget Skra does not ship (nothing to wrap) on Skra tokens — own token tier, state, promotion path | ✅ draft |
|
|
66
66
|
|
package/consumer-guide/layout.md
CHANGED
|
@@ -11,8 +11,12 @@ a native host** (a bounded allowlist of `div`/`section`/`nav`/`ul`/…), **never
|
|
|
11
11
|
`<skra-stack>` custom element. Put them on the semantic element you'd use anyway:
|
|
12
12
|
|
|
13
13
|
```html
|
|
14
|
-
<section skraStack gap="md">…</section>
|
|
15
|
-
|
|
14
|
+
<section skraStack gap="md">…</section>
|
|
15
|
+
<!-- not <div><skra-stack> -->
|
|
16
|
+
<ul skraStack gap="sm">
|
|
17
|
+
…
|
|
18
|
+
</ul>
|
|
19
|
+
<!-- list markers normalized, restorable -->
|
|
16
20
|
```
|
|
17
21
|
|
|
18
22
|
## Sizing container-filling components (e.g. `skra-select`)
|
|
@@ -38,16 +42,16 @@ width.
|
|
|
38
42
|
|
|
39
43
|
Arranges children in a row or column.
|
|
40
44
|
|
|
41
|
-
| Input
|
|
42
|
-
|
|
|
43
|
-
| `direction` | `row` \| `column`
|
|
44
|
-
| `gap`
|
|
45
|
-
| `align`
|
|
46
|
-
| `justify`
|
|
47
|
-
| `wrap`
|
|
45
|
+
| Input | Values | Default |
|
|
46
|
+
| ----------- | ------------------------------------------------------- | --------- |
|
|
47
|
+
| `direction` | `row` \| `column` | `column` |
|
|
48
|
+
| `gap` | `none` \| `sm` \| `md` \| `lg` | `md` |
|
|
49
|
+
| `align` | `start` \| `center` \| `end` \| `stretch` \| `baseline` | `stretch` |
|
|
50
|
+
| `justify` | `start` \| `center` \| `end` \| `between` \| `around` | `start` |
|
|
51
|
+
| `wrap` | boolean | `false` |
|
|
48
52
|
|
|
49
53
|
```html
|
|
50
|
-
<div skraStack direction="row" gap="sm" wrap align="center">
|
|
54
|
+
<div skraStack direction="row" gap="sm" wrap [align]="'center'">
|
|
51
55
|
<skra-chip>One</skra-chip>
|
|
52
56
|
<skra-chip>Two</skra-chip>
|
|
53
57
|
</div>
|
|
@@ -56,13 +60,24 @@ Arranges children in a row or column.
|
|
|
56
60
|
(`wrap` is a bare boolean attribute — every Skra boolean input carries `booleanAttribute`, so
|
|
57
61
|
the bare form works; see `setup-and-gotchas.md`.)
|
|
58
62
|
|
|
63
|
+
**Gotcha — bind `align`, don't write it as a static attribute.** `align` is also a legacy HTML
|
|
64
|
+
attribute. Written statically (`<div skraStack align="center">`), Angular sets the input but the
|
|
65
|
+
attribute stays in the DOM, and the browser turns it into `text-align` on the host — Chromium
|
|
66
|
+
does so on every host Stack accepts (`div`, `section`, `header`, `ul`, …) and for both `center`
|
|
67
|
+
and `end`. Any text inside the stack is then centred or end-aligned. The bound form
|
|
68
|
+
`[align]="'center'"` sets the input without writing the attribute. The same applies to
|
|
69
|
+
`skraGrid`'s `align`. Since O-89 the atoms neutralise the hint themselves (`text-align: inherit`
|
|
70
|
+
while the attribute is present), so the static form no longer misaligns text — the bound form
|
|
71
|
+
stays the recommended one. If you want aligned text inside a stack, set `text-align` on a child
|
|
72
|
+
(or on the host with a selector more specific than a single class).
|
|
73
|
+
|
|
59
74
|
## Box — padding / spacing (`[skraBox]`)
|
|
60
75
|
|
|
61
76
|
A semantically-neutral padding primitive. Does **not** arrange children (no list hosts).
|
|
62
77
|
|
|
63
|
-
| Input
|
|
64
|
-
|
|
|
65
|
-
| `padding` | `none` \| `sm` \| `md` \| `lg`
|
|
78
|
+
| Input | Values | Default |
|
|
79
|
+
| --------- | ----------------------------------- | ------- |
|
|
80
|
+
| `padding` | `none` \| `sm` \| `md` \| `lg` | `none` |
|
|
66
81
|
| `display` | `block` \| `inline-block` \| `flex` | `block` |
|
|
67
82
|
|
|
68
83
|
```html
|
|
@@ -74,8 +89,8 @@ A semantically-neutral padding primitive. Does **not** arrange children (no list
|
|
|
74
89
|
Box's **painting** sibling: background, border, radius, shadow — plus its own padding. Same
|
|
75
90
|
host allowlist as Box (`div`/`section`/`article`/`main`/`nav`/`aside`/`header`/`footer`/`figure`).
|
|
76
91
|
|
|
77
|
-
| Input
|
|
78
|
-
|
|
|
92
|
+
| Input | Values | Default |
|
|
93
|
+
| --------- | ------------------------------ | -------- |
|
|
79
94
|
| `padding` | `none` \| `sm` \| `md` \| `lg` | **`md`** |
|
|
80
95
|
|
|
81
96
|
That is the whole API. Note the default differs from Box's `none` — Box is spacing-neutral by
|
|
@@ -109,16 +124,16 @@ There is no `selected` input, and there never will be one — `aria-selected` is
|
|
|
109
124
|
producing invalid ARIA. Instead, Surface paints a selected state when **your own markup** says
|
|
110
125
|
so, from exactly two signals:
|
|
111
126
|
|
|
112
|
-
| Your markup
|
|
113
|
-
|
|
|
114
|
-
| `aria-current="true"` (or `page` / `step` / `date`) on the Surface host
|
|
115
|
-
| `aria-selected="true"` on a host that **already** carries `option`/`row`/`gridcell`/`tab` | only inside a listbox / grid / tablist you actually built
|
|
127
|
+
| Your markup | When it is the right one |
|
|
128
|
+
| ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
129
|
+
| `aria-current="true"` (or `page` / `step` / `date`) on the Surface host | the current item of a set — a global ARIA state, so it is valid on a role-less `<article>` |
|
|
130
|
+
| `aria-selected="true"` on a host that **already** carries `option`/`row`/`gridcell`/`tab` | only inside a listbox / grid / tablist you actually built |
|
|
116
131
|
|
|
117
132
|
```html
|
|
118
133
|
<article skraSurface padding="md" aria-current="true" aria-labelledby="t">…</article>
|
|
119
134
|
```
|
|
120
135
|
|
|
121
|
-
`aria-current="false"` and `aria-selected="false"` are explicit
|
|
136
|
+
`aria-current="false"` and `aria-selected="false"` are explicit _not_-current and paint as
|
|
122
137
|
resting.
|
|
123
138
|
|
|
124
139
|
**Gotcha — a checked control inside the card is NOT a signal.** A card wrapping a checked
|
|
@@ -133,34 +148,97 @@ there: the radio already announces "selected", and adding `aria-current` makes a
|
|
|
133
148
|
technology announce one fact twice under two names. A dedicated marker for that shape is a known
|
|
134
149
|
gap (Skra-side `[skraSurfaceSelect]`), not something to emulate with a class.
|
|
135
150
|
|
|
136
|
-
### Interactive (clickable) cards
|
|
151
|
+
### Interactive (clickable) cards — `[skraSurfaceLink]`
|
|
137
152
|
|
|
138
153
|
Surface is a container, not a widget, and the host allowlist deliberately excludes `a` and
|
|
139
|
-
`button`: `<button>` cannot legally contain flow content
|
|
140
|
-
|
|
141
|
-
|
|
154
|
+
`button`: `<button>` cannot legally contain flow content, and an `<a>` wrapping the whole card
|
|
155
|
+
takes the card's **entire** text as its accessible name — which `aria-label` may not fix
|
|
156
|
+
(Iron Rule 3). Instead, put `skraSurfaceLink` on **your own** primary link (or `<button>`, when
|
|
157
|
+
the card performs an action) inside the Surface:
|
|
158
|
+
|
|
159
|
+
```html
|
|
160
|
+
<article skraSurface padding="lg">
|
|
161
|
+
<h3 skraTypography="title-small">
|
|
162
|
+
<a skraSurfaceLink routerLink="/runs/2026-08-24">Morning run</a>
|
|
163
|
+
</h3>
|
|
164
|
+
<p skraTypography="body-medium">5.2 km · 24:31</p>
|
|
165
|
+
</article>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
What you get, with no inputs:
|
|
169
|
+
|
|
170
|
+
- The link's hit area covers the whole card; one tab stop; the accessible name is the link
|
|
171
|
+
text ("Morning run"), not the whole card body.
|
|
172
|
+
- Hover fills the card (pointer devices only — it does not latch after a tap), and the focus
|
|
173
|
+
ring is drawn **around the card**, the real target.
|
|
174
|
+
- The link text takes the card's text colour, without an underline (a skin may change that).
|
|
142
175
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
176
|
+
Rules:
|
|
177
|
+
|
|
178
|
+
- **One `skraSurfaceLink` per Surface.** A second one, or one with no Surface ancestor, is a
|
|
179
|
+
dev-mode console error. A list of targets is a list of Surfaces — `<li><article skraSurface>…` — and
|
|
180
|
+
a Surface nested in another paints only itself; the outer panel does not light up.
|
|
181
|
+
- **`skraLink` + `skraSurfaceLink` on one `<a>` is not possible** (both are components). The
|
|
182
|
+
marker already carries the text look.
|
|
183
|
+
- **A second control inside a linked card** (a "Share" button) sits under the overlay unless
|
|
184
|
+
you raise it: give it `position: relative` and keep it after the link in the DOM. Keep it at
|
|
185
|
+
least 24×24 CSS px (WCAG 2.5.8).
|
|
186
|
+
- Text inside a linked card cannot be selected with the mouse — a known, accepted cost of the
|
|
187
|
+
pattern.
|
|
188
|
+
- Do not make a card clickable with `(click)` on a `<div>` — that fails keyboard access
|
|
189
|
+
(WCAG 2.1.1) and has no role.
|
|
190
|
+
|
|
191
|
+
**Hover without a link:** an informational card has no hover state, and there is no
|
|
192
|
+
`hoverable` input. If you add your own hover paint in app CSS, gate it behind
|
|
193
|
+
`@media (hover: hover) and (pointer: fine)`, or it will latch after every tap on touch.
|
|
194
|
+
|
|
195
|
+
## Navigation — `a[skraNavLink]` (ADR 0060)
|
|
196
|
+
|
|
197
|
+
A primary or section navigation whose **current** destination is visible, not only announced.
|
|
198
|
+
`skraNavLink` is a link — middle-click, "open in new tab", "copy link" all stay — with a
|
|
199
|
+
surface shape (pill or square, the skin decides) and a current state:
|
|
200
|
+
|
|
201
|
+
```html
|
|
202
|
+
<nav aria-label="Primary">
|
|
203
|
+
<ul skraStack direction="row" gap="sm">
|
|
204
|
+
<li>
|
|
205
|
+
<a skraNavLink routerLink="/groups" routerLinkActive ariaCurrentWhenActive="page">Groups</a>
|
|
206
|
+
</li>
|
|
207
|
+
<li>
|
|
208
|
+
<a skraNavLink routerLink="/runs" routerLinkActive ariaCurrentWhenActive="page">Runs</a>
|
|
209
|
+
</li>
|
|
210
|
+
</ul>
|
|
211
|
+
</nav>
|
|
212
|
+
```
|
|
146
213
|
|
|
147
|
-
**
|
|
148
|
-
|
|
149
|
-
|
|
214
|
+
- **The current state comes from `aria-current`, which you (or the router) write.**
|
|
215
|
+
`skraNavLink` has no `active` input — it reads the attribute, so what the eye sees and what a
|
|
216
|
+
screen reader announces cannot disagree. Without `routerLinkActive` (or your own
|
|
217
|
+
`[attr.aria-current]`), no item ever shows as current. `aria-current="false"` is not current.
|
|
218
|
+
- **It must sit inside `<nav>`** (or `role="navigation"`); outside one it is a dev-mode console
|
|
219
|
+
error. A pill-shaped link in a hero section is a marketing CTA, which Skra does not ship
|
|
220
|
+
(ADR 032) — use `skraButton` for an action, `skraLink` for a link in text.
|
|
221
|
+
- **No `intent`, `emphasis`, `size` or `disabled`.** A destination the user cannot visit is not a
|
|
222
|
+
link: leave it out of the nav, or render it as plain text.
|
|
223
|
+
- **Name = the visible text.** Do not add `aria-label`.
|
|
224
|
+
- Spacing between items is the list's `skraStack` gap; for a side nav use
|
|
225
|
+
`direction="column"`.
|
|
226
|
+
- `skraLink` vs `skraNavLink`: underlined text in a sentence → `skraLink`; a destination in a
|
|
227
|
+
navigation bar → `skraNavLink`. Do not put both on one `<a>` (both are components).
|
|
150
228
|
|
|
151
229
|
## Grid — 2D (`[skraGrid]`)
|
|
152
230
|
|
|
153
231
|
Bucket-only CSS Grid. The default `columns="auto-fit"` + `min` gives an intrinsically
|
|
154
232
|
responsive card grid (column count tracks width, **no media/container queries**):
|
|
155
233
|
|
|
156
|
-
| Input
|
|
157
|
-
|
|
|
158
|
-
| `columns`
|
|
159
|
-
| `min`
|
|
160
|
-
| `gap`
|
|
161
|
-
| `flow`
|
|
162
|
-
| `align`
|
|
163
|
-
| `justifyItems` | `start` \| `center` \| `end` \| `stretch`
|
|
234
|
+
| Input | Values | Default |
|
|
235
|
+
| -------------- | ------------------------------------------------------------------------------------------------ | ---------- |
|
|
236
|
+
| `columns` | `1`…`6` \| `auto-fit` \| `auto-fill` | `auto-fit` |
|
|
237
|
+
| `min` | `sm` \| `md` \| `lg` | — |
|
|
238
|
+
| `gap` | `none` \| `sm` \| `md` \| `lg` | — |
|
|
239
|
+
| `flow` | `row` \| `column` \| `dense` | — |
|
|
240
|
+
| `align` | `start` \| `center` \| `end` \| `stretch` — bind it: `[align]="'center'"` (see the Stack gotcha) | — |
|
|
241
|
+
| `justifyItems` | `start` \| `center` \| `end` \| `stretch` | — |
|
|
164
242
|
|
|
165
243
|
```html
|
|
166
244
|
<div skraGrid columns="auto-fit" min="md" gap="md">
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, ViewEncapsulation, ChangeDetectionStrategy, Component, booleanAttribute } from '@angular/core';
|
|
2
|
+
import { input, ViewEncapsulation, ChangeDetectionStrategy, Component, booleanAttribute, inject, ElementRef, afterNextRender } from '@angular/core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* SkraBox — semantically-neutral spacing/padding primitive (ADR 041).
|
|
@@ -56,7 +56,7 @@ class SkraStack {
|
|
|
56
56
|
...(ngDevMode ? [{ debugName: "justify" }] : /* istanbul ignore next */ []));
|
|
57
57
|
wrap = input(false, { ...(ngDevMode ? { debugName: "wrap" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
58
58
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraStack, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
59
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.1", type: SkraStack, isStandalone: true, selector: "div[skraStack], section[skraStack], article[skraStack], main[skraStack], nav[skraStack], aside[skraStack], header[skraStack], footer[skraStack], form[skraStack], figure[skraStack], ul[skraStack], ol[skraStack], menu[skraStack]", inputs: { direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, justify: { classPropertyName: "justify", publicName: "justify", isSignal: true, isRequired: false, transformFunction: null }, wrap: { classPropertyName: "wrap", publicName: "wrap", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-skra-direction": "direction()", "attr.data-skra-gap": "gap()", "attr.data-skra-align": "align()", "attr.data-skra-justify": "justify()", "attr.data-skra-wrap": "wrap() ? \"\" : null" } }, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: [":host{display:flex;margin:0;box-sizing:border-box}:host[data-skra-direction=column]{flex-direction:column}:host[data-skra-direction=row]{flex-direction:row}:host[data-skra-gap=none]{gap:0}:host[data-skra-gap=sm]{gap:var(--skra-comp-stack-gap-sm)}:host[data-skra-gap=md]{gap:var(--skra-comp-stack-gap-md)}:host[data-skra-gap=lg]{gap:var(--skra-comp-stack-gap-lg)}:host[data-skra-align=start]{align-items:flex-start}:host[data-skra-align=center]{align-items:center}:host[data-skra-align=end]{align-items:flex-end}:host[data-skra-align=stretch]{align-items:stretch}:host[data-skra-justify=start]{justify-content:flex-start}:host[data-skra-justify=center]{justify-content:center}:host[data-skra-justify=end]{justify-content:flex-end}:host[data-skra-justify=between]{justify-content:space-between}:host[data-skra-justify=around]{justify-content:space-around}:host[data-skra-wrap]{flex-wrap:wrap}:host(ul),:host(ol),:host(menu){list-style:none;padding-inline-start:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
59
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.1", type: SkraStack, isStandalone: true, selector: "div[skraStack], section[skraStack], article[skraStack], main[skraStack], nav[skraStack], aside[skraStack], header[skraStack], footer[skraStack], form[skraStack], figure[skraStack], ul[skraStack], ol[skraStack], menu[skraStack]", inputs: { direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, justify: { classPropertyName: "justify", publicName: "justify", isSignal: true, isRequired: false, transformFunction: null }, wrap: { classPropertyName: "wrap", publicName: "wrap", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-skra-direction": "direction()", "attr.data-skra-gap": "gap()", "attr.data-skra-align": "align()", "attr.data-skra-justify": "justify()", "attr.data-skra-wrap": "wrap() ? \"\" : null" } }, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: [":host{display:flex;margin:0;box-sizing:border-box}:host[data-skra-direction=column]{flex-direction:column}:host[data-skra-direction=row]{flex-direction:row}:host[data-skra-gap=none]{gap:0}:host[data-skra-gap=sm]{gap:var(--skra-comp-stack-gap-sm)}:host[data-skra-gap=md]{gap:var(--skra-comp-stack-gap-md)}:host[data-skra-gap=lg]{gap:var(--skra-comp-stack-gap-lg)}:host[data-skra-align=start]{align-items:flex-start}:host[data-skra-align=center]{align-items:center}:host[data-skra-align=end]{align-items:flex-end}:host[data-skra-align=stretch]{align-items:stretch}:host[data-skra-align=baseline]{align-items:baseline}:host[data-skra-justify=start]{justify-content:flex-start}:host[data-skra-justify=center]{justify-content:center}:host[data-skra-justify=end]{justify-content:flex-end}:host[data-skra-justify=between]{justify-content:space-between}:host[data-skra-justify=around]{justify-content:space-around}:host[data-skra-wrap]{flex-wrap:wrap}:host(ul),:host(ol),:host(menu){list-style:none;padding-inline-start:0}:host(:where([align])){text-align:inherit}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
60
60
|
}
|
|
61
61
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraStack, decorators: [{
|
|
62
62
|
type: Component,
|
|
@@ -70,7 +70,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
|
|
|
70
70
|
'[attr.data-skra-align]': 'align()',
|
|
71
71
|
'[attr.data-skra-justify]': 'justify()',
|
|
72
72
|
'[attr.data-skra-wrap]': 'wrap() ? "" : null',
|
|
73
|
-
}, styles: [":host{display:flex;margin:0;box-sizing:border-box}:host[data-skra-direction=column]{flex-direction:column}:host[data-skra-direction=row]{flex-direction:row}:host[data-skra-gap=none]{gap:0}:host[data-skra-gap=sm]{gap:var(--skra-comp-stack-gap-sm)}:host[data-skra-gap=md]{gap:var(--skra-comp-stack-gap-md)}:host[data-skra-gap=lg]{gap:var(--skra-comp-stack-gap-lg)}:host[data-skra-align=start]{align-items:flex-start}:host[data-skra-align=center]{align-items:center}:host[data-skra-align=end]{align-items:flex-end}:host[data-skra-align=stretch]{align-items:stretch}:host[data-skra-justify=start]{justify-content:flex-start}:host[data-skra-justify=center]{justify-content:center}:host[data-skra-justify=end]{justify-content:flex-end}:host[data-skra-justify=between]{justify-content:space-between}:host[data-skra-justify=around]{justify-content:space-around}:host[data-skra-wrap]{flex-wrap:wrap}:host(ul),:host(ol),:host(menu){list-style:none;padding-inline-start:0}\n"] }]
|
|
73
|
+
}, styles: [":host{display:flex;margin:0;box-sizing:border-box}:host[data-skra-direction=column]{flex-direction:column}:host[data-skra-direction=row]{flex-direction:row}:host[data-skra-gap=none]{gap:0}:host[data-skra-gap=sm]{gap:var(--skra-comp-stack-gap-sm)}:host[data-skra-gap=md]{gap:var(--skra-comp-stack-gap-md)}:host[data-skra-gap=lg]{gap:var(--skra-comp-stack-gap-lg)}:host[data-skra-align=start]{align-items:flex-start}:host[data-skra-align=center]{align-items:center}:host[data-skra-align=end]{align-items:flex-end}:host[data-skra-align=stretch]{align-items:stretch}:host[data-skra-align=baseline]{align-items:baseline}:host[data-skra-justify=start]{justify-content:flex-start}:host[data-skra-justify=center]{justify-content:center}:host[data-skra-justify=end]{justify-content:flex-end}:host[data-skra-justify=between]{justify-content:space-between}:host[data-skra-justify=around]{justify-content:space-around}:host[data-skra-wrap]{flex-wrap:wrap}:host(ul),:host(ol),:host(menu){list-style:none;padding-inline-start:0}:host(:where([align])){text-align:inherit}\n"] }]
|
|
74
74
|
}], propDecorators: { direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], gap: [{ type: i0.Input, args: [{ isSignal: true, alias: "gap", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], justify: [{ type: i0.Input, args: [{ isSignal: true, alias: "justify", required: false }] }], wrap: [{ type: i0.Input, args: [{ isSignal: true, alias: "wrap", required: false }] }] } });
|
|
75
75
|
|
|
76
76
|
/**
|
|
@@ -105,7 +105,7 @@ class SkraGrid {
|
|
|
105
105
|
justifyItems = input('stretch', /* @ts-ignore */
|
|
106
106
|
...(ngDevMode ? [{ debugName: "justifyItems" }] : /* istanbul ignore next */ []));
|
|
107
107
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraGrid, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
108
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.1", type: SkraGrid, isStandalone: true, selector: "div[skraGrid], section[skraGrid], article[skraGrid], main[skraGrid], nav[skraGrid], aside[skraGrid], header[skraGrid], footer[skraGrid], form[skraGrid], figure[skraGrid], ul[skraGrid], ol[skraGrid], menu[skraGrid]", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, flow: { classPropertyName: "flow", publicName: "flow", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, justifyItems: { classPropertyName: "justifyItems", publicName: "justifyItems", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-skra-columns": "columns()", "attr.data-skra-min": "min()", "attr.data-skra-gap": "gap()", "attr.data-skra-flow": "flow()", "attr.data-skra-align": "align()", "attr.data-skra-justify-items": "justifyItems()" } }, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: [":host{display:grid;margin:0;box-sizing:border-box}:host[data-skra-min=sm]{--skra-comp-grid-track-min: var(--skra-comp-grid-min-sm)}:host[data-skra-min=md]{--skra-comp-grid-track-min: var(--skra-comp-grid-min-md)}:host[data-skra-min=lg]{--skra-comp-grid-track-min: var(--skra-comp-grid-min-lg)}:host[data-skra-columns=\"1\"]{grid-template-columns:repeat(1,minmax(0,1fr))}:host[data-skra-columns=\"2\"]{grid-template-columns:repeat(2,minmax(0,1fr))}:host[data-skra-columns=\"3\"]{grid-template-columns:repeat(3,minmax(0,1fr))}:host[data-skra-columns=\"4\"]{grid-template-columns:repeat(4,minmax(0,1fr))}:host[data-skra-columns=\"5\"]{grid-template-columns:repeat(5,minmax(0,1fr))}:host[data-skra-columns=\"6\"]{grid-template-columns:repeat(6,minmax(0,1fr))}:host[data-skra-columns=auto-fit]{grid-template-columns:repeat(auto-fit,minmax(var(--skra-comp-grid-track-min),1fr))}:host[data-skra-columns=auto-fill]{grid-template-columns:repeat(auto-fill,minmax(var(--skra-comp-grid-track-min),1fr))}:host[data-skra-gap=none]{gap:0}:host[data-skra-gap=sm]{gap:var(--skra-comp-grid-gap-sm)}:host[data-skra-gap=md]{gap:var(--skra-comp-grid-gap-md)}:host[data-skra-gap=lg]{gap:var(--skra-comp-grid-gap-lg)}:host[data-skra-flow=row]{grid-auto-flow:row}:host[data-skra-flow=column]{grid-auto-flow:column}:host[data-skra-flow=dense]{grid-auto-flow:row dense}:host[data-skra-align=start]{align-items:start}:host[data-skra-align=center]{align-items:center}:host[data-skra-align=end]{align-items:end}:host[data-skra-align=stretch]{align-items:stretch}:host[data-skra-justify-items=start]{justify-items:start}:host[data-skra-justify-items=center]{justify-items:center}:host[data-skra-justify-items=end]{justify-items:end}:host[data-skra-justify-items=stretch]{justify-items:stretch}:host(ul),:host(ol),:host(menu){list-style:none;padding-inline-start:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
108
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.1", type: SkraGrid, isStandalone: true, selector: "div[skraGrid], section[skraGrid], article[skraGrid], main[skraGrid], nav[skraGrid], aside[skraGrid], header[skraGrid], footer[skraGrid], form[skraGrid], figure[skraGrid], ul[skraGrid], ol[skraGrid], menu[skraGrid]", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, flow: { classPropertyName: "flow", publicName: "flow", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, justifyItems: { classPropertyName: "justifyItems", publicName: "justifyItems", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-skra-columns": "columns()", "attr.data-skra-min": "min()", "attr.data-skra-gap": "gap()", "attr.data-skra-flow": "flow()", "attr.data-skra-align": "align()", "attr.data-skra-justify-items": "justifyItems()" } }, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: [":host{display:grid;margin:0;box-sizing:border-box}:host[data-skra-min=sm]{--skra-comp-grid-track-min: var(--skra-comp-grid-min-sm)}:host[data-skra-min=md]{--skra-comp-grid-track-min: var(--skra-comp-grid-min-md)}:host[data-skra-min=lg]{--skra-comp-grid-track-min: var(--skra-comp-grid-min-lg)}:host[data-skra-columns=\"1\"]{grid-template-columns:repeat(1,minmax(0,1fr))}:host[data-skra-columns=\"2\"]{grid-template-columns:repeat(2,minmax(0,1fr))}:host[data-skra-columns=\"3\"]{grid-template-columns:repeat(3,minmax(0,1fr))}:host[data-skra-columns=\"4\"]{grid-template-columns:repeat(4,minmax(0,1fr))}:host[data-skra-columns=\"5\"]{grid-template-columns:repeat(5,minmax(0,1fr))}:host[data-skra-columns=\"6\"]{grid-template-columns:repeat(6,minmax(0,1fr))}:host[data-skra-columns=auto-fit]{grid-template-columns:repeat(auto-fit,minmax(var(--skra-comp-grid-track-min),1fr))}:host[data-skra-columns=auto-fill]{grid-template-columns:repeat(auto-fill,minmax(var(--skra-comp-grid-track-min),1fr))}:host[data-skra-gap=none]{gap:0}:host[data-skra-gap=sm]{gap:var(--skra-comp-grid-gap-sm)}:host[data-skra-gap=md]{gap:var(--skra-comp-grid-gap-md)}:host[data-skra-gap=lg]{gap:var(--skra-comp-grid-gap-lg)}:host[data-skra-flow=row]{grid-auto-flow:row}:host[data-skra-flow=column]{grid-auto-flow:column}:host[data-skra-flow=dense]{grid-auto-flow:row dense}:host[data-skra-align=start]{align-items:start}:host[data-skra-align=center]{align-items:center}:host[data-skra-align=end]{align-items:end}:host[data-skra-align=stretch]{align-items:stretch}:host[data-skra-justify-items=start]{justify-items:start}:host[data-skra-justify-items=center]{justify-items:center}:host[data-skra-justify-items=end]{justify-items:end}:host[data-skra-justify-items=stretch]{justify-items:stretch}:host(ul),:host(ol),:host(menu){list-style:none;padding-inline-start:0}:host(:where([align])){text-align:inherit}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
109
109
|
}
|
|
110
110
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraGrid, decorators: [{
|
|
111
111
|
type: Component,
|
|
@@ -120,7 +120,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
|
|
|
120
120
|
'[attr.data-skra-flow]': 'flow()',
|
|
121
121
|
'[attr.data-skra-align]': 'align()',
|
|
122
122
|
'[attr.data-skra-justify-items]': 'justifyItems()',
|
|
123
|
-
}, styles: [":host{display:grid;margin:0;box-sizing:border-box}:host[data-skra-min=sm]{--skra-comp-grid-track-min: var(--skra-comp-grid-min-sm)}:host[data-skra-min=md]{--skra-comp-grid-track-min: var(--skra-comp-grid-min-md)}:host[data-skra-min=lg]{--skra-comp-grid-track-min: var(--skra-comp-grid-min-lg)}:host[data-skra-columns=\"1\"]{grid-template-columns:repeat(1,minmax(0,1fr))}:host[data-skra-columns=\"2\"]{grid-template-columns:repeat(2,minmax(0,1fr))}:host[data-skra-columns=\"3\"]{grid-template-columns:repeat(3,minmax(0,1fr))}:host[data-skra-columns=\"4\"]{grid-template-columns:repeat(4,minmax(0,1fr))}:host[data-skra-columns=\"5\"]{grid-template-columns:repeat(5,minmax(0,1fr))}:host[data-skra-columns=\"6\"]{grid-template-columns:repeat(6,minmax(0,1fr))}:host[data-skra-columns=auto-fit]{grid-template-columns:repeat(auto-fit,minmax(var(--skra-comp-grid-track-min),1fr))}:host[data-skra-columns=auto-fill]{grid-template-columns:repeat(auto-fill,minmax(var(--skra-comp-grid-track-min),1fr))}:host[data-skra-gap=none]{gap:0}:host[data-skra-gap=sm]{gap:var(--skra-comp-grid-gap-sm)}:host[data-skra-gap=md]{gap:var(--skra-comp-grid-gap-md)}:host[data-skra-gap=lg]{gap:var(--skra-comp-grid-gap-lg)}:host[data-skra-flow=row]{grid-auto-flow:row}:host[data-skra-flow=column]{grid-auto-flow:column}:host[data-skra-flow=dense]{grid-auto-flow:row dense}:host[data-skra-align=start]{align-items:start}:host[data-skra-align=center]{align-items:center}:host[data-skra-align=end]{align-items:end}:host[data-skra-align=stretch]{align-items:stretch}:host[data-skra-justify-items=start]{justify-items:start}:host[data-skra-justify-items=center]{justify-items:center}:host[data-skra-justify-items=end]{justify-items:end}:host[data-skra-justify-items=stretch]{justify-items:stretch}:host(ul),:host(ol),:host(menu){list-style:none;padding-inline-start:0}\n"] }]
|
|
123
|
+
}, styles: [":host{display:grid;margin:0;box-sizing:border-box}:host[data-skra-min=sm]{--skra-comp-grid-track-min: var(--skra-comp-grid-min-sm)}:host[data-skra-min=md]{--skra-comp-grid-track-min: var(--skra-comp-grid-min-md)}:host[data-skra-min=lg]{--skra-comp-grid-track-min: var(--skra-comp-grid-min-lg)}:host[data-skra-columns=\"1\"]{grid-template-columns:repeat(1,minmax(0,1fr))}:host[data-skra-columns=\"2\"]{grid-template-columns:repeat(2,minmax(0,1fr))}:host[data-skra-columns=\"3\"]{grid-template-columns:repeat(3,minmax(0,1fr))}:host[data-skra-columns=\"4\"]{grid-template-columns:repeat(4,minmax(0,1fr))}:host[data-skra-columns=\"5\"]{grid-template-columns:repeat(5,minmax(0,1fr))}:host[data-skra-columns=\"6\"]{grid-template-columns:repeat(6,minmax(0,1fr))}:host[data-skra-columns=auto-fit]{grid-template-columns:repeat(auto-fit,minmax(var(--skra-comp-grid-track-min),1fr))}:host[data-skra-columns=auto-fill]{grid-template-columns:repeat(auto-fill,minmax(var(--skra-comp-grid-track-min),1fr))}:host[data-skra-gap=none]{gap:0}:host[data-skra-gap=sm]{gap:var(--skra-comp-grid-gap-sm)}:host[data-skra-gap=md]{gap:var(--skra-comp-grid-gap-md)}:host[data-skra-gap=lg]{gap:var(--skra-comp-grid-gap-lg)}:host[data-skra-flow=row]{grid-auto-flow:row}:host[data-skra-flow=column]{grid-auto-flow:column}:host[data-skra-flow=dense]{grid-auto-flow:row dense}:host[data-skra-align=start]{align-items:start}:host[data-skra-align=center]{align-items:center}:host[data-skra-align=end]{align-items:end}:host[data-skra-align=stretch]{align-items:stretch}:host[data-skra-justify-items=start]{justify-items:start}:host[data-skra-justify-items=center]{justify-items:center}:host[data-skra-justify-items=end]{justify-items:end}:host[data-skra-justify-items=stretch]{justify-items:stretch}:host(ul),:host(ol),:host(menu){list-style:none;padding-inline-start:0}:host(:where([align])){text-align:inherit}\n"] }]
|
|
124
124
|
}], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], gap: [{ type: i0.Input, args: [{ isSignal: true, alias: "gap", required: false }] }], flow: [{ type: i0.Input, args: [{ isSignal: true, alias: "flow", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], justifyItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "justifyItems", required: false }] }] } });
|
|
125
125
|
|
|
126
126
|
/**
|
|
@@ -151,7 +151,7 @@ class SkraSurface {
|
|
|
151
151
|
padding = input('md', /* @ts-ignore */
|
|
152
152
|
...(ngDevMode ? [{ debugName: "padding" }] : /* istanbul ignore next */ []));
|
|
153
153
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraSurface, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
154
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.1", type: SkraSurface, isStandalone: true, selector: "div[skraSurface], section[skraSurface], article[skraSurface], main[skraSurface], nav[skraSurface], aside[skraSurface], header[skraSurface], footer[skraSurface], figure[skraSurface]", inputs: { padding: { classPropertyName: "padding", publicName: "padding", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-skra-padding": "padding()" } }, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: [":host{display:block;margin:0;box-sizing:border-box;background:var(--skra-comp-surface-background);color:var(--skra-comp-surface-foreground);border-color:var(--skra-comp-surface-border-color);border-width:var(--skra-comp-surface-border-width);border-style:var(--skra-comp-surface-border-style);border-radius:var(--skra-comp-surface-radius);box-shadow:var(--skra-comp-surface-shadow);padding-block:var(--skra-comp-surface-padding-block);padding-inline:var(--skra-comp-surface-padding-inline);transition:background-color var(--skra-comp-surface-motion-duration) var(--skra-comp-surface-motion-easing),border-color var(--skra-comp-surface-motion-duration) var(--skra-comp-surface-motion-easing)}:host[data-skra-padding=none]{padding-block:0;padding-inline:0}:host[data-skra-padding=sm]{padding-block:var(--skra-comp-surface-padding-block-sm);padding-inline:var(--skra-comp-surface-padding-inline-sm)}:host[data-skra-padding=md]{padding-block:var(--skra-comp-surface-padding-block);padding-inline:var(--skra-comp-surface-padding-inline)}:host[data-skra-padding=lg]{padding-block:var(--skra-comp-surface-padding-block-lg);padding-inline:var(--skra-comp-surface-padding-inline-lg)}:host([aria-current]),:host([aria-selected=true]){background:var(--skra-comp-surface-background-selected);border-color:var(--skra-comp-surface-border-color-selected);border-width:var(--skra-comp-surface-border-width-selected)}:host([aria-current=false]),:host([aria-selected=false]){background:var(--skra-comp-surface-background);border-color:var(--skra-comp-surface-border-color);border-width:var(--skra-comp-surface-border-width)}:host(:focus-visible){outline:var(--skra-comp-surface-focus-ring-width) solid var(--skra-comp-surface-focus-ring-color);outline-offset:var(--skra-comp-surface-focus-ring-width)}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
154
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.1", type: SkraSurface, isStandalone: true, selector: "div[skraSurface], section[skraSurface], article[skraSurface], main[skraSurface], nav[skraSurface], aside[skraSurface], header[skraSurface], footer[skraSurface], figure[skraSurface]", inputs: { padding: { classPropertyName: "padding", publicName: "padding", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-skra-padding": "padding()" } }, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: [":host{display:block;margin:0;box-sizing:border-box;background:var(--skra-comp-surface-background);color:var(--skra-comp-surface-foreground);border-color:var(--skra-comp-surface-border-color);border-width:var(--skra-comp-surface-border-width);border-style:var(--skra-comp-surface-border-style);border-radius:var(--skra-comp-surface-radius);box-shadow:var(--skra-comp-surface-shadow);padding-block:var(--skra-comp-surface-padding-block);padding-inline:var(--skra-comp-surface-padding-inline);transition:background-color var(--skra-comp-surface-motion-duration) var(--skra-comp-surface-motion-easing),border-color var(--skra-comp-surface-motion-duration) var(--skra-comp-surface-motion-easing)}:host[data-skra-padding=none]{padding-block:0;padding-inline:0}:host[data-skra-padding=sm]{padding-block:var(--skra-comp-surface-padding-block-sm);padding-inline:var(--skra-comp-surface-padding-inline-sm)}:host[data-skra-padding=md]{padding-block:var(--skra-comp-surface-padding-block);padding-inline:var(--skra-comp-surface-padding-inline)}:host[data-skra-padding=lg]{padding-block:var(--skra-comp-surface-padding-block-lg);padding-inline:var(--skra-comp-surface-padding-inline-lg)}:host([aria-current]),:host([aria-selected=true]){background:var(--skra-comp-surface-background-selected);border-color:var(--skra-comp-surface-border-color-selected);border-width:var(--skra-comp-surface-border-width-selected)}:host([aria-current=false]),:host([aria-current=\"\"]),:host([aria-current=undefined]),:host([aria-selected=false]){background:var(--skra-comp-surface-background);border-color:var(--skra-comp-surface-border-color);border-width:var(--skra-comp-surface-border-width)}:host(:focus-visible){outline:var(--skra-comp-surface-focus-ring-width) solid var(--skra-comp-surface-focus-ring-color);outline-offset:var(--skra-comp-surface-focus-ring-width)}:host(:has([skraSurfaceLink])){position:relative}@media(hover:hover)and (pointer:fine){:host(:has([skraSurfaceLink]:hover):not(:has([skraSurface] [skraSurfaceLink]:hover)):not([aria-current]:not([aria-current=false]):not([aria-current=\"\"]):not([aria-current=undefined])):not([aria-selected=true])){background:var(--skra-comp-surface-background-hover)}}:host(:has([skraSurfaceLink]:focus-visible):not(:has([skraSurface] [skraSurfaceLink]:focus-visible))){outline:var(--skra-comp-surface-focus-ring-width) solid var(--skra-comp-surface-focus-ring-color);outline-offset:var(--skra-comp-surface-focus-ring-width)}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
155
155
|
}
|
|
156
156
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraSurface, decorators: [{
|
|
157
157
|
type: Component,
|
|
@@ -159,12 +159,78 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
|
|
|
159
159
|
// CSP-Safe State Reflection (ADR 021) — NO inline styles. This is the ONLY
|
|
160
160
|
// host attribute Surface emits (ADR 0055 R2): no role, no tabindex, no aria-*.
|
|
161
161
|
'[attr.data-skra-padding]': 'padding()',
|
|
162
|
-
}, styles: [":host{display:block;margin:0;box-sizing:border-box;background:var(--skra-comp-surface-background);color:var(--skra-comp-surface-foreground);border-color:var(--skra-comp-surface-border-color);border-width:var(--skra-comp-surface-border-width);border-style:var(--skra-comp-surface-border-style);border-radius:var(--skra-comp-surface-radius);box-shadow:var(--skra-comp-surface-shadow);padding-block:var(--skra-comp-surface-padding-block);padding-inline:var(--skra-comp-surface-padding-inline);transition:background-color var(--skra-comp-surface-motion-duration) var(--skra-comp-surface-motion-easing),border-color var(--skra-comp-surface-motion-duration) var(--skra-comp-surface-motion-easing)}:host[data-skra-padding=none]{padding-block:0;padding-inline:0}:host[data-skra-padding=sm]{padding-block:var(--skra-comp-surface-padding-block-sm);padding-inline:var(--skra-comp-surface-padding-inline-sm)}:host[data-skra-padding=md]{padding-block:var(--skra-comp-surface-padding-block);padding-inline:var(--skra-comp-surface-padding-inline)}:host[data-skra-padding=lg]{padding-block:var(--skra-comp-surface-padding-block-lg);padding-inline:var(--skra-comp-surface-padding-inline-lg)}:host([aria-current]),:host([aria-selected=true]){background:var(--skra-comp-surface-background-selected);border-color:var(--skra-comp-surface-border-color-selected);border-width:var(--skra-comp-surface-border-width-selected)}:host([aria-current=false]),:host([aria-selected=false]){background:var(--skra-comp-surface-background);border-color:var(--skra-comp-surface-border-color);border-width:var(--skra-comp-surface-border-width)}:host(:focus-visible){outline:var(--skra-comp-surface-focus-ring-width) solid var(--skra-comp-surface-focus-ring-color);outline-offset:var(--skra-comp-surface-focus-ring-width)}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n"] }]
|
|
162
|
+
}, styles: [":host{display:block;margin:0;box-sizing:border-box;background:var(--skra-comp-surface-background);color:var(--skra-comp-surface-foreground);border-color:var(--skra-comp-surface-border-color);border-width:var(--skra-comp-surface-border-width);border-style:var(--skra-comp-surface-border-style);border-radius:var(--skra-comp-surface-radius);box-shadow:var(--skra-comp-surface-shadow);padding-block:var(--skra-comp-surface-padding-block);padding-inline:var(--skra-comp-surface-padding-inline);transition:background-color var(--skra-comp-surface-motion-duration) var(--skra-comp-surface-motion-easing),border-color var(--skra-comp-surface-motion-duration) var(--skra-comp-surface-motion-easing)}:host[data-skra-padding=none]{padding-block:0;padding-inline:0}:host[data-skra-padding=sm]{padding-block:var(--skra-comp-surface-padding-block-sm);padding-inline:var(--skra-comp-surface-padding-inline-sm)}:host[data-skra-padding=md]{padding-block:var(--skra-comp-surface-padding-block);padding-inline:var(--skra-comp-surface-padding-inline)}:host[data-skra-padding=lg]{padding-block:var(--skra-comp-surface-padding-block-lg);padding-inline:var(--skra-comp-surface-padding-inline-lg)}:host([aria-current]),:host([aria-selected=true]){background:var(--skra-comp-surface-background-selected);border-color:var(--skra-comp-surface-border-color-selected);border-width:var(--skra-comp-surface-border-width-selected)}:host([aria-current=false]),:host([aria-current=\"\"]),:host([aria-current=undefined]),:host([aria-selected=false]){background:var(--skra-comp-surface-background);border-color:var(--skra-comp-surface-border-color);border-width:var(--skra-comp-surface-border-width)}:host(:focus-visible){outline:var(--skra-comp-surface-focus-ring-width) solid var(--skra-comp-surface-focus-ring-color);outline-offset:var(--skra-comp-surface-focus-ring-width)}:host(:has([skraSurfaceLink])){position:relative}@media(hover:hover)and (pointer:fine){:host(:has([skraSurfaceLink]:hover):not(:has([skraSurface] [skraSurfaceLink]:hover)):not([aria-current]:not([aria-current=false]):not([aria-current=\"\"]):not([aria-current=undefined])):not([aria-selected=true])){background:var(--skra-comp-surface-background-hover)}}:host(:has([skraSurfaceLink]:focus-visible):not(:has([skraSurface] [skraSurfaceLink]:focus-visible))){outline:var(--skra-comp-surface-focus-ring-width) solid var(--skra-comp-surface-focus-ring-color);outline-offset:var(--skra-comp-surface-focus-ring-width)}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n"] }]
|
|
163
163
|
}], propDecorators: { padding: [{ type: i0.Input, args: [{ isSignal: true, alias: "padding", required: false }] }] } });
|
|
164
164
|
|
|
165
|
+
/**
|
|
166
|
+
* SkraSurfaceLink — the stretched-link marker that makes a whole `SkraSurface`
|
|
167
|
+
* one activation target (ADR 0055 R3/R4).
|
|
168
|
+
*
|
|
169
|
+
* Placed on the consumer's OWN primary control — `<a>` for navigation, `<button>`
|
|
170
|
+
* where the destination is an action — never on the Surface host (R3.1:
|
|
171
|
+
* `a[skraSurface]` / `button[skraSurface]` stay unsupported). Its own host
|
|
172
|
+
* stylesheet stretches the control's hit area over the nearest Surface with a
|
|
173
|
+
* `::after` overlay; the Surface reads `:has([skraSurfaceLink]…)` and paints hover
|
|
174
|
+
* and the focus ring on itself, because the surface is the target (R4.2).
|
|
175
|
+
*
|
|
176
|
+
* Accessible name: the control's own visible text (ADR 033 Path 1) — normally the
|
|
177
|
+
* card's heading. No inputs, no `aria-*`, no `role`, no `tabindex`: the native
|
|
178
|
+
* host already is the one tab stop.
|
|
179
|
+
*
|
|
180
|
+
* Dev-mode console errors (R3.3): no `[skraSurface]` ancestor (the overlay would
|
|
181
|
+
* stretch over whatever positioned ancestor happens to exist — possibly the whole
|
|
182
|
+
* page), and more than one marker whose NEAREST Surface is the same (two overlays
|
|
183
|
+
* = an invisible hit-area conflict). Checked on the rendered DOM, not through DI:
|
|
184
|
+
* a consumer's own card component projecting content into `<article skraSurface>`
|
|
185
|
+
* puts the marker inside the Surface in the DOM while its injector chain follows
|
|
186
|
+
* the declaring template and never meets the Surface.
|
|
187
|
+
*/
|
|
188
|
+
class SkraSurfaceLink {
|
|
189
|
+
constructor() {
|
|
190
|
+
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
|
191
|
+
const host = inject(ElementRef).nativeElement;
|
|
192
|
+
// Browser-only (afterNextRender never runs on the server) and read-only: the
|
|
193
|
+
// DOM is inspected, never mutated.
|
|
194
|
+
afterNextRender(() => checkPlacement(host));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraSurfaceLink, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
198
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.1", type: SkraSurfaceLink, isStandalone: true, selector: "a[skraSurfaceLink], button[skraSurfaceLink]", ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: [":host{position:static;margin:0;color:inherit;text-decoration:var(--skra-comp-surface-link-text-decoration)}:host(button){padding:0;border:0;background:none;font:inherit;text-align:inherit;cursor:pointer}:host:after{content:\"\";position:absolute;inset:0}:host(:focus-visible){outline:none}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
199
|
+
}
|
|
200
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraSurfaceLink, decorators: [{
|
|
201
|
+
type: Component,
|
|
202
|
+
args: [{ selector: 'a[skraSurfaceLink], button[skraSurfaceLink]', standalone: true, template: '<ng-content></ng-content>', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, styles: [":host{position:static;margin:0;color:inherit;text-decoration:var(--skra-comp-surface-link-text-decoration)}:host(button){padding:0;border:0;background:none;font:inherit;text-align:inherit;cursor:pointer}:host:after{content:\"\";position:absolute;inset:0}:host(:focus-visible){outline:none}\n"] }]
|
|
203
|
+
}], ctorParameters: () => [] });
|
|
204
|
+
/** Markers already reported as duplicates, per Surface — so one conflict logs once. */
|
|
205
|
+
const reportedMarkers = new WeakMap();
|
|
206
|
+
/** Dev-mode placement check (ADR 0055 R3 / R3.3). */
|
|
207
|
+
function checkPlacement(host) {
|
|
208
|
+
const surface = host.parentElement?.closest('[skraSurface]');
|
|
209
|
+
if (!surface) {
|
|
210
|
+
console.error('[skraSurfaceLink] must sit inside a [skraSurface] host — its overlay stretches ' +
|
|
211
|
+
'over the nearest Surface, and without one it would cover whatever positioned ' +
|
|
212
|
+
'ancestor exists (ADR 0055 R3).', host);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const own = Array.from(surface.querySelectorAll('[skraSurfaceLink]')).filter((link) => link.parentElement?.closest('[skraSurface]') === surface);
|
|
216
|
+
// Reported once per conflict, by whichever marker renders into it — including one
|
|
217
|
+
// inserted BEFORE an existing marker, which a "not the first in DOM order" rule
|
|
218
|
+
// missed (PR #9 review, S2). A marker that has already been reported stays quiet.
|
|
219
|
+
if (own.length > 1) {
|
|
220
|
+
const seen = reportedMarkers.get(surface) ?? new WeakSet();
|
|
221
|
+
reportedMarkers.set(surface, seen);
|
|
222
|
+
if (own.every((link) => seen.has(link)))
|
|
223
|
+
return;
|
|
224
|
+
own.forEach((link) => seen.add(link));
|
|
225
|
+
console.error('[skraSurfaceLink] appears more than once in the same [skraSurface] — at most one ' +
|
|
226
|
+
'stretched link per surface (ADR 0055 R3.3). Make each target its own Surface, ' +
|
|
227
|
+
'or keep the secondary control a plain link/button raised above the overlay.', host);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
165
231
|
/**
|
|
166
232
|
* Generated bundle index. Do not edit.
|
|
167
233
|
*/
|
|
168
234
|
|
|
169
|
-
export { SkraBox, SkraGrid, SkraStack, SkraSurface };
|
|
235
|
+
export { SkraBox, SkraGrid, SkraStack, SkraSurface, SkraSurfaceLink };
|
|
170
236
|
//# sourceMappingURL=skra-ui-layout.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skra-ui-layout.mjs","sources":["../../../../libs/ui/layout/src/lib/box/box.ts","../../../../libs/ui/layout/src/lib/stack/stack.ts","../../../../libs/ui/layout/src/lib/grid/grid.ts","../../../../libs/ui/layout/src/lib/surface/surface.ts","../../../../libs/ui/layout/src/skra-ui-layout.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n input,\n} from '@angular/core';\n\nexport type BoxPadding = 'none' | 'sm' | 'md' | 'lg';\nexport type BoxDisplay = 'block' | 'inline-block' | 'flex';\n\n/**\n * SkraBox — semantically-neutral spacing/padding primitive (ADR 041).\n *\n * Component-on-native-host: layers onto a generic flow/sectioning container via\n * the bounded host allowlist below — NEVER a `<skra-box>` custom element\n * (ADR 012 §3.1 / ADR 041). Box does NOT arrange children, so list hosts\n * (`ul`/`ol`/`menu`) are intentionally excluded from its allowlist.\n *\n * Box is a pure spacing primitive: it MUST NOT establish a container-query\n * context (`container-type`/`container-name`) — that is an app-level concern\n * (ADR 016 §3.3, amended by ADR 041).\n */\n@Component({\n selector:\n 'div[skraBox], section[skraBox], article[skraBox], main[skraBox], nav[skraBox], aside[skraBox], header[skraBox], footer[skraBox], form[skraBox], figure[skraBox]',\n standalone: true,\n template: '<ng-content></ng-content>',\n styleUrl: './box.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n host: {\n // CSP-Safe State Reflection (ADR 021) — NO inline styles! The layout reset\n // (margin/box-sizing) + figure UA-margin normalization live in box.scss\n // `:host` (ADR 022 / ADR 041 Host Normalization Contract); host-metadata\n // bare keys would render as junk attributes, not styles.\n '[attr.data-skra-padding]': 'padding()',\n '[attr.data-skra-display]': 'display()',\n },\n})\nexport class SkraBox {\n readonly padding = input<BoxPadding>('none');\n readonly display = input<BoxDisplay>('block');\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n booleanAttribute,\n input,\n} from '@angular/core';\n\nexport type StackDirection = 'row' | 'column';\nexport type StackGap = 'none' | 'sm' | 'md' | 'lg';\nexport type StackAlign = 'start' | 'center' | 'end' | 'stretch';\nexport type StackJustify = 'start' | 'center' | 'end' | 'between' | 'around';\n\n/**\n * SkraStack — flex layout primitive that arranges its children (ADR 041).\n *\n * Component-on-native-host via the bounded allowlist below. As a child-arranger\n * it additionally accepts list hosts (`ul`/`ol`/`menu`) — their UA list styling\n * is normalized in stack.scss per the Host Normalization Contract. List markers\n * are restorable without `!important` via a direct `li {}` rule (a direct\n * declaration beats the inherited `list-style: none`).\n *\n * Stack consumes `@container`; it MUST NOT establish a container-query context\n * (ADR 016 §3.3, amended by ADR 041).\n */\n@Component({\n selector:\n 'div[skraStack], section[skraStack], article[skraStack], main[skraStack], nav[skraStack], aside[skraStack], header[skraStack], footer[skraStack], form[skraStack], figure[skraStack], ul[skraStack], ol[skraStack], menu[skraStack]',\n standalone: true,\n template: '<ng-content></ng-content>',\n styleUrl: './stack.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n host: {\n // CSP-Safe State Reflection — only data-attributes, no [style.*]. The\n // layout reset (margin/box-sizing) + list/UA normalization live in\n // stack.scss `:host` (ADR 022 / ADR 041 Host Normalization Contract);\n // host-metadata bare keys would render as junk attributes, not styles.\n '[attr.data-skra-direction]': 'direction()',\n '[attr.data-skra-gap]': 'gap()',\n '[attr.data-skra-align]': 'align()',\n '[attr.data-skra-justify]': 'justify()',\n '[attr.data-skra-wrap]': 'wrap() ? \"\" : null',\n },\n})\nexport class SkraStack {\n readonly direction = input<StackDirection>('column');\n readonly gap = input<StackGap>('md');\n readonly align = input<StackAlign>('stretch');\n readonly justify = input<StackJustify>('start');\n readonly wrap = input(false, { transform: booleanAttribute });\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n input,\n} from '@angular/core';\n\nexport type GridColumns =\n | '1'\n | '2'\n | '3'\n | '4'\n | '5'\n | '6'\n | 'auto-fit'\n | 'auto-fill';\nexport type GridMin = 'sm' | 'md' | 'lg';\nexport type GridGap = 'none' | 'sm' | 'md' | 'lg';\nexport type GridFlow = 'row' | 'column' | 'dense';\nexport type GridAlign = 'start' | 'center' | 'end' | 'stretch';\n\n/**\n * SkraGrid — 2D layout primitive that arranges its children in a CSS Grid\n * (ADR 041, scope O-3). The bucket-only companion to Stack (1D) and Box (spacing).\n *\n * Component-on-native-host via the bounded allowlist below. As a child-arranger\n * it additionally accepts list hosts (`ul`/`ol`/`menu`) — their UA list styling\n * is normalized in grid.scss per the Host Normalization Contract.\n *\n * Bucket-only by design: every axis is an enumerated input reflected as a\n * `data-skra-*` attribute → token. Arbitrary track lists, `grid-template-areas`,\n * named lines and per-item placement are OUT of scope (bespoke = native CSS) —\n * exposing them would force an `[style.*]` host binding (ADR 021 violation).\n *\n * Responsiveness: `columns=\"auto-fit\"` (default) + `min` produce an intrinsically\n * responsive card grid via `repeat(auto-fit, minmax(var(--min), 1fr))` — column\n * count tracks available width with NO media/container queries. Grid consumes\n * `@container`; it MUST NOT establish a container-query context (ADR 041 §3.3).\n */\n@Component({\n selector:\n 'div[skraGrid], section[skraGrid], article[skraGrid], main[skraGrid], nav[skraGrid], aside[skraGrid], header[skraGrid], footer[skraGrid], form[skraGrid], figure[skraGrid], ul[skraGrid], ol[skraGrid], menu[skraGrid]',\n standalone: true,\n template: '<ng-content></ng-content>',\n styleUrl: './grid.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n host: {\n // CSP-Safe State Reflection — only data-attributes, no [style.*]. The layout\n // reset (margin/box-sizing/display:grid) + list/UA normalization live in\n // grid.scss `:host` (ADR 022 / ADR 041 Host Normalization Contract);\n // host-metadata bare keys would render as junk attributes, not styles.\n '[attr.data-skra-columns]': 'columns()',\n '[attr.data-skra-min]': 'min()',\n '[attr.data-skra-gap]': 'gap()',\n '[attr.data-skra-flow]': 'flow()',\n '[attr.data-skra-align]': 'align()',\n '[attr.data-skra-justify-items]': 'justifyItems()',\n },\n})\nexport class SkraGrid {\n readonly columns = input<GridColumns>('auto-fit');\n readonly min = input<GridMin>('md');\n readonly gap = input<GridGap>('md');\n readonly flow = input<GridFlow>('row');\n readonly align = input<GridAlign>('stretch');\n readonly justifyItems = input<GridAlign>('stretch');\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n input,\n} from '@angular/core';\n\nexport type SurfacePadding = 'none' | 'sm' | 'md' | 'lg';\n\n/**\n * SkraSurface — the painted container primitive (ADR 0053), Box's painting sibling.\n *\n * Component-on-native-host: layers onto a generic flow/sectioning container via the\n * bounded host allowlist below — NEVER a `<skra-surface>` custom element, and never\n * an interactive host (ADR 0055 R3.1: `a[skraSurface]` / `button[skraSurface]` are\n * unsupported). \"Card\", \"Panel\" and \"Tile\" are consumer compositions of Surface +\n * `[skraStack]`/`[skraBox]` + a heading — not Skra components (ADR 0053 rule 1).\n *\n * **State-reading doctrine (ADR 0055).** Surface is a READER of state, never an\n * OWNER of state. It exposes no `interactive`/`hoverable`/`selected`/`elevation`\n * input and emits no `role`/`tabindex`/`aria-*`. Focus and selection are READ from\n * the markup the consumer authored (see surface.scss) — which is why an\n * informational card gets no interactive paint with zero API, and why a selected\n * card can never disagree with what assistive technology announces.\n *\n * Zero baked semantics: sectioning role and accessible name are 100% consumer-owned\n * (ADR 0053 rule 6). No `container-type` in v1 (ADR 0055 R7.1).\n */\n@Component({\n selector:\n 'div[skraSurface], section[skraSurface], article[skraSurface], main[skraSurface], nav[skraSurface], aside[skraSurface], header[skraSurface], footer[skraSurface], figure[skraSurface]',\n standalone: true,\n template: '<ng-content></ng-content>',\n styleUrl: './surface.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n host: {\n // CSP-Safe State Reflection (ADR 021) — NO inline styles. This is the ONLY\n // host attribute Surface emits (ADR 0055 R2): no role, no tabindex, no aria-*.\n '[attr.data-skra-padding]': 'padding()',\n },\n})\nexport class SkraSurface {\n /**\n * Semantic padding preset, reusing Box's enum for cross-atom consistency\n * (ADR 0053 rule 4). Defaults to `md` rather than Box's `none` — Box is\n * spacing-neutral by construction, a painted container is not (ADR 0055 R1).\n */\n readonly padding = input<SurfacePadding>('md');\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"skra-ui-layout.mjs","sources":["../../../../libs/ui/layout/src/lib/box/box.ts","../../../../libs/ui/layout/src/lib/stack/stack.ts","../../../../libs/ui/layout/src/lib/grid/grid.ts","../../../../libs/ui/layout/src/lib/surface/surface.ts","../../../../libs/ui/layout/src/lib/surface/surface-link.ts","../../../../libs/ui/layout/src/skra-ui-layout.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n input,\n} from '@angular/core';\n\nexport type BoxPadding = 'none' | 'sm' | 'md' | 'lg';\nexport type BoxDisplay = 'block' | 'inline-block' | 'flex';\n\n/**\n * SkraBox — semantically-neutral spacing/padding primitive (ADR 041).\n *\n * Component-on-native-host: layers onto a generic flow/sectioning container via\n * the bounded host allowlist below — NEVER a `<skra-box>` custom element\n * (ADR 012 §3.1 / ADR 041). Box does NOT arrange children, so list hosts\n * (`ul`/`ol`/`menu`) are intentionally excluded from its allowlist.\n *\n * Box is a pure spacing primitive: it MUST NOT establish a container-query\n * context (`container-type`/`container-name`) — that is an app-level concern\n * (ADR 016 §3.3, amended by ADR 041).\n */\n@Component({\n selector:\n 'div[skraBox], section[skraBox], article[skraBox], main[skraBox], nav[skraBox], aside[skraBox], header[skraBox], footer[skraBox], form[skraBox], figure[skraBox]',\n standalone: true,\n template: '<ng-content></ng-content>',\n styleUrl: './box.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n host: {\n // CSP-Safe State Reflection (ADR 021) — NO inline styles! The layout reset\n // (margin/box-sizing) + figure UA-margin normalization live in box.scss\n // `:host` (ADR 022 / ADR 041 Host Normalization Contract); host-metadata\n // bare keys would render as junk attributes, not styles.\n '[attr.data-skra-padding]': 'padding()',\n '[attr.data-skra-display]': 'display()',\n },\n})\nexport class SkraBox {\n readonly padding = input<BoxPadding>('none');\n readonly display = input<BoxDisplay>('block');\n}\n","import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ViewEncapsulation,\r\n booleanAttribute,\r\n input,\r\n} from '@angular/core';\r\n\r\nexport type StackDirection = 'row' | 'column';\r\nexport type StackGap = 'none' | 'sm' | 'md' | 'lg';\r\nexport type StackAlign = 'start' | 'center' | 'end' | 'stretch' | 'baseline';\r\nexport type StackJustify = 'start' | 'center' | 'end' | 'between' | 'around';\r\n\r\n/**\r\n * SkraStack — flex layout primitive that arranges its children (ADR 041).\r\n *\r\n * Component-on-native-host via the bounded allowlist below. As a child-arranger\r\n * it additionally accepts list hosts (`ul`/`ol`/`menu`) — their UA list styling\r\n * is normalized in stack.scss per the Host Normalization Contract. List markers\r\n * are restorable without `!important` via a direct `li {}` rule (a direct\r\n * declaration beats the inherited `list-style: none`).\r\n *\r\n * Stack consumes `@container`; it MUST NOT establish a container-query context\r\n * (ADR 016 §3.3, amended by ADR 041).\r\n */\r\n@Component({\r\n selector:\r\n 'div[skraStack], section[skraStack], article[skraStack], main[skraStack], nav[skraStack], aside[skraStack], header[skraStack], footer[skraStack], form[skraStack], figure[skraStack], ul[skraStack], ol[skraStack], menu[skraStack]',\r\n standalone: true,\r\n template: '<ng-content></ng-content>',\r\n styleUrl: './stack.scss',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.Emulated,\r\n host: {\r\n // CSP-Safe State Reflection — only data-attributes, no [style.*]. The\r\n // layout reset (margin/box-sizing) + list/UA normalization live in\r\n // stack.scss `:host` (ADR 022 / ADR 041 Host Normalization Contract);\r\n // host-metadata bare keys would render as junk attributes, not styles.\r\n '[attr.data-skra-direction]': 'direction()',\r\n '[attr.data-skra-gap]': 'gap()',\r\n '[attr.data-skra-align]': 'align()',\r\n '[attr.data-skra-justify]': 'justify()',\r\n '[attr.data-skra-wrap]': 'wrap() ? \"\" : null',\r\n },\r\n})\r\nexport class SkraStack {\r\n readonly direction = input<StackDirection>('column');\r\n readonly gap = input<StackGap>('md');\r\n readonly align = input<StackAlign>('stretch');\r\n readonly justify = input<StackJustify>('start');\r\n readonly wrap = input(false, { transform: booleanAttribute });\r\n}\r\n","import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n input,\n} from '@angular/core';\n\nexport type GridColumns =\n | '1'\n | '2'\n | '3'\n | '4'\n | '5'\n | '6'\n | 'auto-fit'\n | 'auto-fill';\nexport type GridMin = 'sm' | 'md' | 'lg';\nexport type GridGap = 'none' | 'sm' | 'md' | 'lg';\nexport type GridFlow = 'row' | 'column' | 'dense';\nexport type GridAlign = 'start' | 'center' | 'end' | 'stretch';\n\n/**\n * SkraGrid — 2D layout primitive that arranges its children in a CSS Grid\n * (ADR 041, scope O-3). The bucket-only companion to Stack (1D) and Box (spacing).\n *\n * Component-on-native-host via the bounded allowlist below. As a child-arranger\n * it additionally accepts list hosts (`ul`/`ol`/`menu`) — their UA list styling\n * is normalized in grid.scss per the Host Normalization Contract.\n *\n * Bucket-only by design: every axis is an enumerated input reflected as a\n * `data-skra-*` attribute → token. Arbitrary track lists, `grid-template-areas`,\n * named lines and per-item placement are OUT of scope (bespoke = native CSS) —\n * exposing them would force an `[style.*]` host binding (ADR 021 violation).\n *\n * Responsiveness: `columns=\"auto-fit\"` (default) + `min` produce an intrinsically\n * responsive card grid via `repeat(auto-fit, minmax(var(--min), 1fr))` — column\n * count tracks available width with NO media/container queries. Grid consumes\n * `@container`; it MUST NOT establish a container-query context (ADR 041 §3.3).\n */\n@Component({\n selector:\n 'div[skraGrid], section[skraGrid], article[skraGrid], main[skraGrid], nav[skraGrid], aside[skraGrid], header[skraGrid], footer[skraGrid], form[skraGrid], figure[skraGrid], ul[skraGrid], ol[skraGrid], menu[skraGrid]',\n standalone: true,\n template: '<ng-content></ng-content>',\n styleUrl: './grid.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n host: {\n // CSP-Safe State Reflection — only data-attributes, no [style.*]. The layout\n // reset (margin/box-sizing/display:grid) + list/UA normalization live in\n // grid.scss `:host` (ADR 022 / ADR 041 Host Normalization Contract);\n // host-metadata bare keys would render as junk attributes, not styles.\n '[attr.data-skra-columns]': 'columns()',\n '[attr.data-skra-min]': 'min()',\n '[attr.data-skra-gap]': 'gap()',\n '[attr.data-skra-flow]': 'flow()',\n '[attr.data-skra-align]': 'align()',\n '[attr.data-skra-justify-items]': 'justifyItems()',\n },\n})\nexport class SkraGrid {\n readonly columns = input<GridColumns>('auto-fit');\n readonly min = input<GridMin>('md');\n readonly gap = input<GridGap>('md');\n readonly flow = input<GridFlow>('row');\n readonly align = input<GridAlign>('stretch');\n readonly justifyItems = input<GridAlign>('stretch');\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n input,\n} from '@angular/core';\n\nexport type SurfacePadding = 'none' | 'sm' | 'md' | 'lg';\n\n/**\n * SkraSurface — the painted container primitive (ADR 0053), Box's painting sibling.\n *\n * Component-on-native-host: layers onto a generic flow/sectioning container via the\n * bounded host allowlist below — NEVER a `<skra-surface>` custom element, and never\n * an interactive host (ADR 0055 R3.1: `a[skraSurface]` / `button[skraSurface]` are\n * unsupported). \"Card\", \"Panel\" and \"Tile\" are consumer compositions of Surface +\n * `[skraStack]`/`[skraBox]` + a heading — not Skra components (ADR 0053 rule 1).\n *\n * **State-reading doctrine (ADR 0055).** Surface is a READER of state, never an\n * OWNER of state. It exposes no `interactive`/`hoverable`/`selected`/`elevation`\n * input and emits no `role`/`tabindex`/`aria-*`. Focus and selection are READ from\n * the markup the consumer authored (see surface.scss) — which is why an\n * informational card gets no interactive paint with zero API, and why a selected\n * card can never disagree with what assistive technology announces.\n *\n * Zero baked semantics: sectioning role and accessible name are 100% consumer-owned\n * (ADR 0053 rule 6). No `container-type` in v1 (ADR 0055 R7.1).\n */\n@Component({\n selector:\n 'div[skraSurface], section[skraSurface], article[skraSurface], main[skraSurface], nav[skraSurface], aside[skraSurface], header[skraSurface], footer[skraSurface], figure[skraSurface]',\n standalone: true,\n template: '<ng-content></ng-content>',\n styleUrl: './surface.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n host: {\n // CSP-Safe State Reflection (ADR 021) — NO inline styles. This is the ONLY\n // host attribute Surface emits (ADR 0055 R2): no role, no tabindex, no aria-*.\n '[attr.data-skra-padding]': 'padding()',\n },\n})\nexport class SkraSurface {\n /**\n * Semantic padding preset, reusing Box's enum for cross-atom consistency\n * (ADR 0053 rule 4). Defaults to `md` rather than Box's `none` — Box is\n * spacing-neutral by construction, a painted container is not (ADR 0055 R1).\n */\n readonly padding = input<SurfacePadding>('md');\n}\n","import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ElementRef,\r\n ViewEncapsulation,\r\n afterNextRender,\r\n inject,\r\n} from '@angular/core';\r\n\r\ndeclare const ngDevMode: boolean | undefined;\r\n\r\n/**\r\n * SkraSurfaceLink — the stretched-link marker that makes a whole `SkraSurface`\r\n * one activation target (ADR 0055 R3/R4).\r\n *\r\n * Placed on the consumer's OWN primary control — `<a>` for navigation, `<button>`\r\n * where the destination is an action — never on the Surface host (R3.1:\r\n * `a[skraSurface]` / `button[skraSurface]` stay unsupported). Its own host\r\n * stylesheet stretches the control's hit area over the nearest Surface with a\r\n * `::after` overlay; the Surface reads `:has([skraSurfaceLink]…)` and paints hover\r\n * and the focus ring on itself, because the surface is the target (R4.2).\r\n *\r\n * Accessible name: the control's own visible text (ADR 033 Path 1) — normally the\r\n * card's heading. No inputs, no `aria-*`, no `role`, no `tabindex`: the native\r\n * host already is the one tab stop.\r\n *\r\n * Dev-mode console errors (R3.3): no `[skraSurface]` ancestor (the overlay would\r\n * stretch over whatever positioned ancestor happens to exist — possibly the whole\r\n * page), and more than one marker whose NEAREST Surface is the same (two overlays\r\n * = an invisible hit-area conflict). Checked on the rendered DOM, not through DI:\r\n * a consumer's own card component projecting content into `<article skraSurface>`\r\n * puts the marker inside the Surface in the DOM while its injector chain follows\r\n * the declaring template and never meets the Surface.\r\n */\r\n@Component({\r\n selector: 'a[skraSurfaceLink], button[skraSurfaceLink]',\r\n standalone: true,\r\n template: '<ng-content></ng-content>',\r\n styleUrl: './surface-link.scss',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.Emulated,\r\n})\r\nexport class SkraSurfaceLink {\r\n constructor() {\r\n if (typeof ngDevMode === 'undefined' || ngDevMode) {\r\n const host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\r\n // Browser-only (afterNextRender never runs on the server) and read-only: the\r\n // DOM is inspected, never mutated.\r\n afterNextRender(() => checkPlacement(host));\r\n }\r\n }\r\n}\r\n\r\n/** Markers already reported as duplicates, per Surface — so one conflict logs once. */\r\nconst reportedMarkers = new WeakMap<Element, WeakSet<Element>>();\r\n\r\n/** Dev-mode placement check (ADR 0055 R3 / R3.3). */\r\nfunction checkPlacement(host: HTMLElement): void {\r\n const surface = host.parentElement?.closest('[skraSurface]');\r\n if (!surface) {\r\n console.error(\r\n '[skraSurfaceLink] must sit inside a [skraSurface] host — its overlay stretches ' +\r\n 'over the nearest Surface, and without one it would cover whatever positioned ' +\r\n 'ancestor exists (ADR 0055 R3).',\r\n host,\r\n );\r\n return;\r\n }\r\n const own = Array.from(surface.querySelectorAll('[skraSurfaceLink]')).filter(\r\n (link) => link.parentElement?.closest('[skraSurface]') === surface,\r\n );\r\n // Reported once per conflict, by whichever marker renders into it — including one\r\n // inserted BEFORE an existing marker, which a \"not the first in DOM order\" rule\r\n // missed (PR #9 review, S2). A marker that has already been reported stays quiet.\r\n if (own.length > 1) {\r\n const seen = reportedMarkers.get(surface) ?? new WeakSet<Element>();\r\n reportedMarkers.set(surface, seen);\r\n if (own.every((link) => seen.has(link))) return;\r\n own.forEach((link) => seen.add(link));\r\n console.error(\r\n '[skraSurfaceLink] appears more than once in the same [skraSurface] — at most one ' +\r\n 'stretched link per surface (ADR 0055 R3.3). Make each target its own Surface, ' +\r\n 'or keep the secondary control a plain link/button raised above the overlay.',\r\n host,\r\n );\r\n }\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
2
|
+
import { ViewEncapsulation, ChangeDetectionStrategy, Component, inject, ElementRef, afterNextRender } from '@angular/core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* SkraLink — text-style anchor primitive (ADR 032 §3.6).
|
|
@@ -21,9 +21,69 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
|
|
|
21
21
|
args: [{ selector: 'a[skraLink]', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, template: "<ng-content></ng-content>\n", styles: [":host{margin:0;color:var(--skra-comp-link-foreground);text-decoration-line:var(--skra-comp-link-text-decoration-line);text-decoration-thickness:var(--skra-comp-link-text-decoration-thickness);text-underline-offset:var(--skra-comp-link-text-underline-offset);transition:color var(--skra-comp-link-motion-duration) var(--skra-comp-link-motion-easing)}:host:visited{color:var(--skra-comp-link-foreground-visited)}@media(hover:hover)and (pointer:fine){:host:hover{color:var(--skra-comp-link-foreground-hover)}}:host:active{color:var(--skra-comp-link-foreground-active)}:host:focus-visible{outline-style:solid;outline-color:var(--skra-comp-link-focus-ring-color);outline-width:var(--skra-comp-link-focus-ring-width);outline-offset:2px}\n"] }]
|
|
22
22
|
}] });
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* SkraNavLink — a navigation destination with a visible current state (ADR 0060).
|
|
26
|
+
*
|
|
27
|
+
* Selector: `a[skraNavLink]` — `<a>` is the sole host (R1: a nav item that does not
|
|
28
|
+
* navigate is not a nav item). A separate atom from `skraLink`: the surface shape and
|
|
29
|
+
* the current state belong to navigation chrome, `:visited` and the underline to prose.
|
|
30
|
+
*
|
|
31
|
+
* **A navigation link is a link.** It keeps every native `<a>` behaviour and owns no
|
|
32
|
+
* state: zero inputs, zero outputs, no host attributes. The current state is READ from
|
|
33
|
+
* the native `aria-current` the consumer or the router already sets
|
|
34
|
+
* (`routerLinkActive` + `ariaCurrentWhenActive="page"`) — the component never writes it,
|
|
35
|
+
* so the paint and what assistive technology announces cannot disagree (R3).
|
|
36
|
+
*
|
|
37
|
+
* No `intent`/`emphasis`/`size` (R2 — the guardrail that keeps the marketing CTA out,
|
|
38
|
+
* ADR 032 §3.5), no `disabled` (ADR 032 §3.6.3), no `ariaLabel` (ADR 033 Path 1: the
|
|
39
|
+
* visible text is the name).
|
|
40
|
+
*
|
|
41
|
+
* Dev-mode console error (R5.1): a nav link outside a navigation landmark (`<nav>` or
|
|
42
|
+
* `role="navigation"`). Checked on the rendered DOM, not through DI — a consumer
|
|
43
|
+
* component that projects the link into its own `<nav>` must not trip it.
|
|
44
|
+
*/
|
|
45
|
+
class SkraNavLink {
|
|
46
|
+
constructor() {
|
|
47
|
+
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
|
48
|
+
const host = inject(ElementRef).nativeElement;
|
|
49
|
+
// Browser-only (afterNextRender never runs on the server) and read-only.
|
|
50
|
+
afterNextRender(() => {
|
|
51
|
+
if (!inNavigation(host)) {
|
|
52
|
+
console.error('[skraNavLink] must sit inside a navigation landmark (<nav> or ' +
|
|
53
|
+
'role="navigation") — outside one, a pill-shaped link is the marketing CTA ' +
|
|
54
|
+
'that ADR 032 §3.5 keeps unsupported (ADR 0060 R5.1).', host);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraNavLink, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
60
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.1", type: SkraNavLink, isStandalone: true, selector: "a[skraNavLink]", ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: [":host{display:inline-flex;align-items:center;margin:0;box-sizing:border-box;background:var(--skra-comp-nav-link-background);color:var(--skra-comp-nav-link-foreground);border-color:var(--skra-comp-nav-link-border-color);border-width:var(--skra-comp-nav-link-border-width);border-style:var(--skra-comp-nav-link-border-style);border-radius:var(--skra-comp-nav-link-radius);box-shadow:var(--skra-comp-nav-link-shadow);padding-block:var(--skra-comp-nav-link-padding-block);padding-inline:var(--skra-comp-nav-link-padding-inline);font-family:var(--skra-sys-typography-role-label-large-font-family);font-size:var(--skra-sys-typography-role-label-large-font-size);font-weight:var(--skra-sys-typography-role-label-large-font-weight);line-height:var(--skra-sys-typography-role-label-large-line-height);letter-spacing:var(--skra-sys-typography-role-label-large-letter-spacing);text-decoration:none;transition:background-color var(--skra-comp-nav-link-motion-duration) var(--skra-comp-nav-link-motion-easing),color var(--skra-comp-nav-link-motion-duration) var(--skra-comp-nav-link-motion-easing),border-color var(--skra-comp-nav-link-motion-duration) var(--skra-comp-nav-link-motion-easing)}:host([aria-current]:not([aria-current=false]):not([aria-current=\"\"]):not([aria-current=undefined])){background:var(--skra-comp-nav-link-background-current);color:var(--skra-comp-nav-link-foreground-current);border-color:var(--skra-comp-nav-link-border-color-current);border-width:var(--skra-comp-nav-link-border-width-current);padding-block:calc(var(--skra-comp-nav-link-padding-block) - (var(--skra-comp-nav-link-border-width-current) - var(--skra-comp-nav-link-border-width)));padding-inline:calc(var(--skra-comp-nav-link-padding-inline) - (var(--skra-comp-nav-link-border-width-current) - var(--skra-comp-nav-link-border-width)))}@media(hover:hover)and (pointer:fine){:host(:hover:not([aria-current]:not([aria-current=false]):not([aria-current=\"\"]):not([aria-current=undefined]))){background:var(--skra-comp-nav-link-background-hover);color:var(--skra-comp-nav-link-foreground-hover)}}:host(:focus-visible){outline:var(--skra-comp-nav-link-focus-ring-width) solid var(--skra-comp-nav-link-focus-ring-color);outline-offset:var(--skra-comp-nav-link-focus-ring-width)}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
61
|
+
}
|
|
62
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraNavLink, decorators: [{
|
|
63
|
+
type: Component,
|
|
64
|
+
args: [{ selector: 'a[skraNavLink]', standalone: true, template: '<ng-content></ng-content>', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, styles: [":host{display:inline-flex;align-items:center;margin:0;box-sizing:border-box;background:var(--skra-comp-nav-link-background);color:var(--skra-comp-nav-link-foreground);border-color:var(--skra-comp-nav-link-border-color);border-width:var(--skra-comp-nav-link-border-width);border-style:var(--skra-comp-nav-link-border-style);border-radius:var(--skra-comp-nav-link-radius);box-shadow:var(--skra-comp-nav-link-shadow);padding-block:var(--skra-comp-nav-link-padding-block);padding-inline:var(--skra-comp-nav-link-padding-inline);font-family:var(--skra-sys-typography-role-label-large-font-family);font-size:var(--skra-sys-typography-role-label-large-font-size);font-weight:var(--skra-sys-typography-role-label-large-font-weight);line-height:var(--skra-sys-typography-role-label-large-line-height);letter-spacing:var(--skra-sys-typography-role-label-large-letter-spacing);text-decoration:none;transition:background-color var(--skra-comp-nav-link-motion-duration) var(--skra-comp-nav-link-motion-easing),color var(--skra-comp-nav-link-motion-duration) var(--skra-comp-nav-link-motion-easing),border-color var(--skra-comp-nav-link-motion-duration) var(--skra-comp-nav-link-motion-easing)}:host([aria-current]:not([aria-current=false]):not([aria-current=\"\"]):not([aria-current=undefined])){background:var(--skra-comp-nav-link-background-current);color:var(--skra-comp-nav-link-foreground-current);border-color:var(--skra-comp-nav-link-border-color-current);border-width:var(--skra-comp-nav-link-border-width-current);padding-block:calc(var(--skra-comp-nav-link-padding-block) - (var(--skra-comp-nav-link-border-width-current) - var(--skra-comp-nav-link-border-width)));padding-inline:calc(var(--skra-comp-nav-link-padding-inline) - (var(--skra-comp-nav-link-border-width-current) - var(--skra-comp-nav-link-border-width)))}@media(hover:hover)and (pointer:fine){:host(:hover:not([aria-current]:not([aria-current=false]):not([aria-current=\"\"]):not([aria-current=undefined]))){background:var(--skra-comp-nav-link-background-hover);color:var(--skra-comp-nav-link-foreground-hover)}}:host(:focus-visible){outline:var(--skra-comp-nav-link-focus-ring-width) solid var(--skra-comp-nav-link-focus-ring-color);outline-offset:var(--skra-comp-nav-link-focus-ring-width)}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n"] }]
|
|
65
|
+
}], ctorParameters: () => [] });
|
|
66
|
+
/**
|
|
67
|
+
* True when `el` sits inside a navigation landmark in the FLAT tree. `closest()` stops at a
|
|
68
|
+
* shadow root, but assistive technology does not: a link rendered in the shadow root of a
|
|
69
|
+
* consumer component that itself sits inside `<nav>` IS navigation. So the walk hops from
|
|
70
|
+
* a shadow root to its host and continues.
|
|
71
|
+
*/
|
|
72
|
+
function inNavigation(el) {
|
|
73
|
+
let node = el;
|
|
74
|
+
while (node) {
|
|
75
|
+
const hit = node.closest('nav, [role="navigation"]');
|
|
76
|
+
if (hit)
|
|
77
|
+
return true;
|
|
78
|
+
const root = node.getRootNode();
|
|
79
|
+
node = root instanceof ShadowRoot ? root.host : null;
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
|
|
24
84
|
/**
|
|
25
85
|
* Generated bundle index. Do not edit.
|
|
26
86
|
*/
|
|
27
87
|
|
|
28
|
-
export { SkraLink };
|
|
88
|
+
export { SkraLink, SkraNavLink };
|
|
29
89
|
//# sourceMappingURL=skra-ui-link.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skra-ui-link.mjs","sources":["../../../../libs/ui/link/src/lib/link/link.ts","../../../../libs/ui/link/src/lib/link/link.html","../../../../libs/ui/link/src/skra-ui-link.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n} from '@angular/core';\n\n/**\n * SkraLink — text-style anchor primitive (ADR 032 §3.6).\n *\n * Selector: `a[skraLink]` — `<a>` is the sole host.\n *\n * Per ADR 032 §3.6.3 there is no `disabled` input and no `data-skra-*`\n * Availability reflection: a link the user cannot or should not follow should\n * not be a link in the first place. Severity is deferred beyond MVP (§3.6.4).\n * Accessible Name follows ADR 033 Path 1 — visible text is the accessible name;\n * the component exposes no `ariaLabel` input.\n */\n@Component({\n selector: 'a[skraLink]',\n standalone: true,\n templateUrl: './link.html',\n styleUrl: './link.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n})\nexport class SkraLink {}\n","<ng-content></ng-content>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAMA;;;;;;;;;;AAUG;MASU,QAAQ,CAAA;uGAAR,QAAQ,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAR,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,QAAQ,uECzBrB,6BACA,EAAA,MAAA,EAAA,CAAA,8tBAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FDwBa,QAAQ,EAAA,UAAA,EAAA,CAAA;kBARpB,SAAS;+BACE,aAAa,EAAA,UAAA,EACX,IAAI,EAAA,eAAA,EAGC,uBAAuB,CAAC,MAAM,EAAA,aAAA,EAChC,iBAAiB,CAAC,QAAQ,EAAA,QAAA,EAAA,6BAAA,EAAA,MAAA,EAAA,CAAA,8tBAAA,CAAA,EAAA;;;
|
|
1
|
+
{"version":3,"file":"skra-ui-link.mjs","sources":["../../../../libs/ui/link/src/lib/link/link.ts","../../../../libs/ui/link/src/lib/link/link.html","../../../../libs/ui/link/src/lib/nav-link/nav-link.ts","../../../../libs/ui/link/src/skra-ui-link.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n} from '@angular/core';\n\n/**\n * SkraLink — text-style anchor primitive (ADR 032 §3.6).\n *\n * Selector: `a[skraLink]` — `<a>` is the sole host.\n *\n * Per ADR 032 §3.6.3 there is no `disabled` input and no `data-skra-*`\n * Availability reflection: a link the user cannot or should not follow should\n * not be a link in the first place. Severity is deferred beyond MVP (§3.6.4).\n * Accessible Name follows ADR 033 Path 1 — visible text is the accessible name;\n * the component exposes no `ariaLabel` input.\n */\n@Component({\n selector: 'a[skraLink]',\n standalone: true,\n templateUrl: './link.html',\n styleUrl: './link.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n})\nexport class SkraLink {}\n","<ng-content></ng-content>\n","import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ElementRef,\r\n ViewEncapsulation,\r\n afterNextRender,\r\n inject,\r\n} from '@angular/core';\r\n\r\ndeclare const ngDevMode: boolean | undefined;\r\n\r\n/**\r\n * SkraNavLink — a navigation destination with a visible current state (ADR 0060).\r\n *\r\n * Selector: `a[skraNavLink]` — `<a>` is the sole host (R1: a nav item that does not\r\n * navigate is not a nav item). A separate atom from `skraLink`: the surface shape and\r\n * the current state belong to navigation chrome, `:visited` and the underline to prose.\r\n *\r\n * **A navigation link is a link.** It keeps every native `<a>` behaviour and owns no\r\n * state: zero inputs, zero outputs, no host attributes. The current state is READ from\r\n * the native `aria-current` the consumer or the router already sets\r\n * (`routerLinkActive` + `ariaCurrentWhenActive=\"page\"`) — the component never writes it,\r\n * so the paint and what assistive technology announces cannot disagree (R3).\r\n *\r\n * No `intent`/`emphasis`/`size` (R2 — the guardrail that keeps the marketing CTA out,\r\n * ADR 032 §3.5), no `disabled` (ADR 032 §3.6.3), no `ariaLabel` (ADR 033 Path 1: the\r\n * visible text is the name).\r\n *\r\n * Dev-mode console error (R5.1): a nav link outside a navigation landmark (`<nav>` or\r\n * `role=\"navigation\"`). Checked on the rendered DOM, not through DI — a consumer\r\n * component that projects the link into its own `<nav>` must not trip it.\r\n */\r\n@Component({\r\n selector: 'a[skraNavLink]',\r\n standalone: true,\r\n template: '<ng-content></ng-content>',\r\n styleUrl: './nav-link.scss',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.Emulated,\r\n})\r\nexport class SkraNavLink {\r\n constructor() {\r\n if (typeof ngDevMode === 'undefined' || ngDevMode) {\r\n const host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\r\n // Browser-only (afterNextRender never runs on the server) and read-only.\r\n afterNextRender(() => {\r\n if (!inNavigation(host)) {\r\n console.error(\r\n '[skraNavLink] must sit inside a navigation landmark (<nav> or ' +\r\n 'role=\"navigation\") — outside one, a pill-shaped link is the marketing CTA ' +\r\n 'that ADR 032 §3.5 keeps unsupported (ADR 0060 R5.1).',\r\n host,\r\n );\r\n }\r\n });\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * True when `el` sits inside a navigation landmark in the FLAT tree. `closest()` stops at a\r\n * shadow root, but assistive technology does not: a link rendered in the shadow root of a\r\n * consumer component that itself sits inside `<nav>` IS navigation. So the walk hops from\r\n * a shadow root to its host and continues.\r\n */\r\nfunction inNavigation(el: Element): boolean {\r\n let node: Element | null = el;\r\n while (node) {\r\n const hit = node.closest('nav, [role=\"navigation\"]');\r\n if (hit) return true;\r\n const root = node.getRootNode();\r\n node = root instanceof ShadowRoot ? root.host : null;\r\n }\r\n return false;\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAMA;;;;;;;;;;AAUG;MASU,QAAQ,CAAA;uGAAR,QAAQ,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAR,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,QAAQ,uECzBrB,6BACA,EAAA,MAAA,EAAA,CAAA,8tBAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FDwBa,QAAQ,EAAA,UAAA,EAAA,CAAA;kBARpB,SAAS;+BACE,aAAa,EAAA,UAAA,EACX,IAAI,EAAA,eAAA,EAGC,uBAAuB,CAAC,MAAM,EAAA,aAAA,EAChC,iBAAiB,CAAC,QAAQ,EAAA,QAAA,EAAA,6BAAA,EAAA,MAAA,EAAA,CAAA,8tBAAA,CAAA,EAAA;;;AEZ3C;;;;;;;;;;;;;;;;;;;;AAoBG;MASU,WAAW,CAAA;AACtB,IAAA,WAAA,GAAA;AACE,QAAA,IAAI,OAAO,SAAS,KAAK,WAAW,IAAI,SAAS,EAAE;YACjD,MAAM,IAAI,GAAG,MAAM,CAA0B,UAAU,CAAC,CAAC,aAAa;;YAEtE,eAAe,CAAC,MAAK;AACnB,gBAAA,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE;oBACvB,OAAO,CAAC,KAAK,CACX,gEAAgE;wBAC9D,4EAA4E;wBAC5E,sDAAsD,EACxD,IAAI,CACL;gBACH;AACF,YAAA,CAAC,CAAC;QACJ;IACF;uGAhBW,WAAW,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAX,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,WAAW,0EALZ,2BAA2B,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,8wEAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAK1B,WAAW,EAAA,UAAA,EAAA,CAAA;kBARvB,SAAS;+BACE,gBAAgB,EAAA,UAAA,EACd,IAAI,EAAA,QAAA,EACN,2BAA2B,EAAA,eAAA,EAEpB,uBAAuB,CAAC,MAAM,EAAA,aAAA,EAChC,iBAAiB,CAAC,QAAQ,EAAA,MAAA,EAAA,CAAA,8wEAAA,CAAA,EAAA;;AAqB3C;;;;;AAKG;AACH,SAAS,YAAY,CAAC,EAAW,EAAA;IAC/B,IAAI,IAAI,GAAmB,EAAE;IAC7B,OAAO,IAAI,EAAE;QACX,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,0BAA0B,CAAC;AACpD,QAAA,IAAI,GAAG;AAAE,YAAA,OAAO,IAAI;AACpB,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE;AAC/B,QAAA,IAAI,GAAG,IAAI,YAAY,UAAU,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI;IACtD;AACA,IAAA,OAAO,KAAK;AACd;;AC1EA;;AAEG;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skra/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"@angular/core": ">=22.0.0 <23.0.0",
|
|
11
11
|
"@angular/common": ">=22.0.0 <23.0.0",
|
|
12
12
|
"@angular/cdk": ">=22.0.0 <23.0.0",
|
|
13
|
-
"@skra/tokens": "^0.
|
|
13
|
+
"@skra/tokens": "^0.9.0"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@floating-ui/dom": "^1.7.6",
|
|
@@ -23,7 +23,7 @@ declare class SkraBox {
|
|
|
23
23
|
|
|
24
24
|
type StackDirection = 'row' | 'column';
|
|
25
25
|
type StackGap = 'none' | 'sm' | 'md' | 'lg';
|
|
26
|
-
type StackAlign = 'start' | 'center' | 'end' | 'stretch';
|
|
26
|
+
type StackAlign = 'start' | 'center' | 'end' | 'stretch' | 'baseline';
|
|
27
27
|
type StackJustify = 'start' | 'center' | 'end' | 'between' | 'around';
|
|
28
28
|
/**
|
|
29
29
|
* SkraStack — flex layout primitive that arranges its children (ADR 041).
|
|
@@ -112,6 +112,35 @@ declare class SkraSurface {
|
|
|
112
112
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SkraSurface, "div[skraSurface], section[skraSurface], article[skraSurface], main[skraSurface], nav[skraSurface], aside[skraSurface], header[skraSurface], footer[skraSurface], figure[skraSurface]", never, { "padding": { "alias": "padding"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
/**
|
|
116
|
+
* SkraSurfaceLink — the stretched-link marker that makes a whole `SkraSurface`
|
|
117
|
+
* one activation target (ADR 0055 R3/R4).
|
|
118
|
+
*
|
|
119
|
+
* Placed on the consumer's OWN primary control — `<a>` for navigation, `<button>`
|
|
120
|
+
* where the destination is an action — never on the Surface host (R3.1:
|
|
121
|
+
* `a[skraSurface]` / `button[skraSurface]` stay unsupported). Its own host
|
|
122
|
+
* stylesheet stretches the control's hit area over the nearest Surface with a
|
|
123
|
+
* `::after` overlay; the Surface reads `:has([skraSurfaceLink]…)` and paints hover
|
|
124
|
+
* and the focus ring on itself, because the surface is the target (R4.2).
|
|
125
|
+
*
|
|
126
|
+
* Accessible name: the control's own visible text (ADR 033 Path 1) — normally the
|
|
127
|
+
* card's heading. No inputs, no `aria-*`, no `role`, no `tabindex`: the native
|
|
128
|
+
* host already is the one tab stop.
|
|
129
|
+
*
|
|
130
|
+
* Dev-mode console errors (R3.3): no `[skraSurface]` ancestor (the overlay would
|
|
131
|
+
* stretch over whatever positioned ancestor happens to exist — possibly the whole
|
|
132
|
+
* page), and more than one marker whose NEAREST Surface is the same (two overlays
|
|
133
|
+
* = an invisible hit-area conflict). Checked on the rendered DOM, not through DI:
|
|
134
|
+
* a consumer's own card component projecting content into `<article skraSurface>`
|
|
135
|
+
* puts the marker inside the Surface in the DOM while its injector chain follows
|
|
136
|
+
* the declaring template and never meets the Surface.
|
|
137
|
+
*/
|
|
138
|
+
declare class SkraSurfaceLink {
|
|
139
|
+
constructor();
|
|
140
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SkraSurfaceLink, never>;
|
|
141
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SkraSurfaceLink, "a[skraSurfaceLink], button[skraSurfaceLink]", never, {}, {}, never, ["*"], true, never>;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export { SkraBox, SkraGrid, SkraStack, SkraSurface, SkraSurfaceLink };
|
|
116
145
|
export type { BoxDisplay, BoxPadding, GridAlign, GridColumns, GridFlow, GridGap, GridMin, StackAlign, StackDirection, StackGap, StackJustify, SurfacePadding };
|
|
117
146
|
//# sourceMappingURL=skra-ui-layout.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skra-ui-layout.d.ts","sources":["../../../../libs/ui/layout/src/lib/box/box.ts","../../../../libs/ui/layout/src/lib/stack/stack.ts","../../../../libs/ui/layout/src/lib/grid/grid.ts","../../../../libs/ui/layout/src/lib/surface/surface.ts"],"mappings":";;AAOM,KAAM,UAAU;AAChB,KAAM,UAAU;AAEtB;;;;;;;;;;;AAWG;AACH,cAiBa,OAAO;sBACF,aAAA,CAAA,WAAA,CAAA,UAAA;sBACA,aAAA,CAAA,WAAA,CAAA,UAAA;oDAFL,OAAO;sDAAP,OAAO;AAGnB;;KClCW,cAAc;AACpB,KAAM,QAAQ;AACd,KAAM,UAAU;AAChB,KAAM,YAAY;AAExB;;;;;;;;;;;AAWG;AACH,cAoBa,SAAS;wBACF,aAAA,CAAA,WAAA,CAAA,cAAA;kBACN,aAAA,CAAA,WAAA,CAAA,QAAA;oBACE,aAAA,CAAA,WAAA,CAAA,UAAA;sBACE,aAAA,CAAA,WAAA,CAAA,YAAA;mBACH,aAAA,CAAA,wBAAA;oDALF,SAAS;sDAAT,SAAS;AAMrB;;KC5CW,WAAW;AASjB,KAAM,OAAO;AACb,KAAM,OAAO;AACb,KAAM,QAAQ;AACd,KAAM,SAAS;AAErB;;;;;;;;;;;;;;;;;AAiBG;AACH,cAqBa,QAAQ;sBACH,aAAA,CAAA,WAAA,CAAA,WAAA;kBACJ,aAAA,CAAA,WAAA,CAAA,OAAA;kBACA,aAAA,CAAA,WAAA,CAAA,OAAA;mBACC,aAAA,CAAA,WAAA,CAAA,QAAA;oBACC,aAAA,CAAA,WAAA,CAAA,SAAA;2BACO,aAAA,CAAA,WAAA,CAAA,SAAA;oDANV,QAAQ;sDAAR,QAAQ;AAOpB;;AC5DK,KAAM,cAAc;AAE1B;;;;;;;;;;;;;;;;;;AAkBG;AACH,cAca,WAAW;AACtB;;;;AAIG;sBACa,aAAA,CAAA,WAAA,CAAA,cAAA;oDANL,WAAW;sDAAX,WAAW;AAOvB;;;;","names":[]}
|
|
1
|
+
{"version":3,"file":"skra-ui-layout.d.ts","sources":["../../../../libs/ui/layout/src/lib/box/box.ts","../../../../libs/ui/layout/src/lib/stack/stack.ts","../../../../libs/ui/layout/src/lib/grid/grid.ts","../../../../libs/ui/layout/src/lib/surface/surface.ts","../../../../libs/ui/layout/src/lib/surface/surface-link.ts"],"mappings":";;AAOM,KAAM,UAAU;AAChB,KAAM,UAAU;AAEtB;;;;;;;;;;;AAWG;AACH,cAiBa,OAAO;sBACF,aAAA,CAAA,WAAA,CAAA,UAAA;sBACA,aAAA,CAAA,WAAA,CAAA,UAAA;oDAFL,OAAO;sDAAP,OAAO;AAGnB;;KClCW,cAAc;AACpB,KAAM,QAAQ;AACd,KAAM,UAAU;AAChB,KAAM,YAAY;AAExB;;;;;;;;;;;AAWG;AACH,cAoBa,SAAS;wBACF,aAAA,CAAA,WAAA,CAAA,cAAA;kBACN,aAAA,CAAA,WAAA,CAAA,QAAA;oBACE,aAAA,CAAA,WAAA,CAAA,UAAA;sBACE,aAAA,CAAA,WAAA,CAAA,YAAA;mBACH,aAAA,CAAA,wBAAA;oDALF,SAAS;sDAAT,SAAS;AAMrB;;KC5CW,WAAW;AASjB,KAAM,OAAO;AACb,KAAM,OAAO;AACb,KAAM,QAAQ;AACd,KAAM,SAAS;AAErB;;;;;;;;;;;;;;;;;AAiBG;AACH,cAqBa,QAAQ;sBACH,aAAA,CAAA,WAAA,CAAA,WAAA;kBACJ,aAAA,CAAA,WAAA,CAAA,OAAA;kBACA,aAAA,CAAA,WAAA,CAAA,OAAA;mBACC,aAAA,CAAA,WAAA,CAAA,QAAA;oBACC,aAAA,CAAA,WAAA,CAAA,SAAA;2BACO,aAAA,CAAA,WAAA,CAAA,SAAA;oDANV,QAAQ;sDAAR,QAAQ;AAOpB;;AC5DK,KAAM,cAAc;AAE1B;;;;;;;;;;;;;;;;;;AAkBG;AACH,cAca,WAAW;AACtB;;;;AAIG;sBACa,aAAA,CAAA,WAAA,CAAA,cAAA;oDANL,WAAW;sDAAX,WAAW;AAOvB;;ACtCD;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACH,cAQa,eAAe;;oDAAf,eAAe;sDAAf,eAAe;AAS3B;;;;","names":[]}
|
package/types/skra-ui-link.d.ts
CHANGED
|
@@ -16,5 +16,32 @@ declare class SkraLink {
|
|
|
16
16
|
static ɵcmp: i0.ɵɵComponentDeclaration<SkraLink, "a[skraLink]", never, {}, {}, never, ["*"], true, never>;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
/**
|
|
20
|
+
* SkraNavLink — a navigation destination with a visible current state (ADR 0060).
|
|
21
|
+
*
|
|
22
|
+
* Selector: `a[skraNavLink]` — `<a>` is the sole host (R1: a nav item that does not
|
|
23
|
+
* navigate is not a nav item). A separate atom from `skraLink`: the surface shape and
|
|
24
|
+
* the current state belong to navigation chrome, `:visited` and the underline to prose.
|
|
25
|
+
*
|
|
26
|
+
* **A navigation link is a link.** It keeps every native `<a>` behaviour and owns no
|
|
27
|
+
* state: zero inputs, zero outputs, no host attributes. The current state is READ from
|
|
28
|
+
* the native `aria-current` the consumer or the router already sets
|
|
29
|
+
* (`routerLinkActive` + `ariaCurrentWhenActive="page"`) — the component never writes it,
|
|
30
|
+
* so the paint and what assistive technology announces cannot disagree (R3).
|
|
31
|
+
*
|
|
32
|
+
* No `intent`/`emphasis`/`size` (R2 — the guardrail that keeps the marketing CTA out,
|
|
33
|
+
* ADR 032 §3.5), no `disabled` (ADR 032 §3.6.3), no `ariaLabel` (ADR 033 Path 1: the
|
|
34
|
+
* visible text is the name).
|
|
35
|
+
*
|
|
36
|
+
* Dev-mode console error (R5.1): a nav link outside a navigation landmark (`<nav>` or
|
|
37
|
+
* `role="navigation"`). Checked on the rendered DOM, not through DI — a consumer
|
|
38
|
+
* component that projects the link into its own `<nav>` must not trip it.
|
|
39
|
+
*/
|
|
40
|
+
declare class SkraNavLink {
|
|
41
|
+
constructor();
|
|
42
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SkraNavLink, never>;
|
|
43
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<SkraNavLink, "a[skraNavLink]", never, {}, {}, never, ["*"], true, never>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export { SkraLink, SkraNavLink };
|
|
20
47
|
//# sourceMappingURL=skra-ui-link.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skra-ui-link.d.ts","sources":["../../../../libs/ui/link/src/lib/link/link.ts"],"mappings":";;AAMA;;;;;;;;;;AAUG;AACH,cAQa,QAAQ;yCAAR,QAAQ;2CAAR,QAAQ;AAAG;;;;","names":[]}
|
|
1
|
+
{"version":3,"file":"skra-ui-link.d.ts","sources":["../../../../libs/ui/link/src/lib/link/link.ts","../../../../libs/ui/link/src/lib/nav-link/nav-link.ts"],"mappings":";;AAMA;;;;;;;;;;AAUG;AACH,cAQa,QAAQ;yCAAR,QAAQ;2CAAR,QAAQ;AAAG;;ACdxB;;;;;;;;;;;;;;;;;;;;AAoBG;AACH,cAQa,WAAW;;yCAAX,WAAW;2CAAX,WAAW;AAiBvB;;;;","names":[]}
|