@cahyo-dimas/freeday 3.5.0 → 3.7.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/CHANGELOG.md +130 -0
- package/COMPONENTS.md +251 -8
- package/README.id.md +19 -6
- package/README.md +17 -5
- package/USAGE.md +42 -0
- package/adapters/blazor/FdyDateRange.razor.cs +10 -0
- package/adapters/blazor/FdyTable.razor +6 -2
- package/adapters/blazor/FdyTable.razor.cs +21 -0
- package/adapters/blazor/TableTypes.cs +4 -0
- package/adapters/react/components/FdyDateRange.tsx +8 -2
- package/adapters/react/components/FdyTable.tsx +24 -3
- package/adapters/react/index.d.ts +1 -1
- package/adapters/vue/components/FdyDateRange.vue +11 -5
- package/adapters/vue/components/FdyTable.vue +18 -2
- package/dist/freeday-confirm.js +101 -0
- package/dist/freeday-copy.js +58 -0
- package/dist/freeday-datepicker.js +17 -2
- package/dist/freeday-form.js +30 -1
- package/dist/freeday-search.js +77 -0
- package/dist/freeday.bundle.css +544 -122
- package/dist/freeday.css +241 -118
- package/dist/freeday.d.ts +17 -2
- package/dist/freeday.js +286 -3
- package/dist/freeday.tokens.css +303 -4
- package/docs/agent-onboarding.md +8 -0
- package/docs/getting-started.md +1 -1
- package/package.json +2 -2
- package/src/base.css +1 -1
- package/src/components/accordion.css +1 -1
- package/src/components/alert.css +5 -5
- package/src/components/app-shell.css +2 -2
- package/src/components/appbar.css +3 -3
- package/src/components/avatar.css +17 -9
- package/src/components/badge.css +18 -14
- package/src/components/breadcrumb.css +1 -1
- package/src/components/button.css +21 -13
- package/src/components/card.css +7 -0
- package/src/components/carousel.css +3 -3
- package/src/components/cascade.css +4 -4
- package/src/components/chip.css +33 -15
- package/src/components/combo.css +5 -5
- package/src/components/composition.css +36 -0
- package/src/components/datepicker.css +8 -8
- package/src/components/description-list.css +11 -1
- package/src/components/file-upload.css +9 -4
- package/src/components/input-group.css +2 -2
- package/src/components/input.css +2 -2
- package/src/components/menu.css +3 -3
- package/src/components/pagination.css +1 -1
- package/src/components/selection.css +8 -2
- package/src/components/skeleton.css +1 -1
- package/src/components/states.css +7 -0
- package/src/components/stepper.css +1 -1
- package/src/components/table.css +25 -11
- package/src/components/tabs.css +1 -1
- package/src/components/timepicker.css +3 -3
- package/src/components/toast.css +1 -1
- package/src/components/tree.css +1 -1
- package/tokens/tokens.json +57 -4
package/docs/agent-onboarding.md
CHANGED
|
@@ -137,6 +137,14 @@ Recent additions most likely to replace something an app hand-rolled (all detail
|
|
|
137
137
|
| `size="cfl"` on `FdyModal` | writing `.fdy-modal--cfl` by hand to reach the 46rem width |
|
|
138
138
|
| `dialogOnly` + `open()` on `FdyCfl` | a hand-rolled picker, because the trigger is a chip or a table cell and not a 22rem field |
|
|
139
139
|
| `.fdy-field--full` | `max-width: none` on the form container, once per form, because the fields are a flex column and not a `.fdy-form-grid` |
|
|
140
|
+
| `.fdy-page__columns` | a hand-rolled 8/4 or 3/6/3 grid per screen, with a summary rail that falls under every section on a phone |
|
|
141
|
+
| `.fdy-dl--grid` | a facts grid whose columns follow the window, so a side rail gets two 110px columns |
|
|
142
|
+
| `.fdy-card__head` | a card title row with its "View all" link at the bottom, moving every time the content grows |
|
|
143
|
+
| `settled` on `FdyDateRange` | reloading a list on each end of a range, the first time with the start alone |
|
|
144
|
+
| `{ filtered, clearFilters }` on `FdyTable`'s empty state | one "No data" sentence for both "nothing matches" and "nothing yet" |
|
|
145
|
+
| `Freeday.confirm()` | a confirm dialog assembled from a modal, two buttons and a flag so it answers only once |
|
|
146
|
+
| `[data-fdy-search]` | a search box that only searches on Enter, so emptying it leaves the old rows on screen |
|
|
147
|
+
| `[data-fdy-copy]` | selecting an ID by hand, or a copy button that silently does nothing over http |
|
|
140
148
|
| `.fdy-nav--flat` | undoing the caret, the pointer cursor and the group divider yourself, on a sidebar whose group labels never collapse |
|
|
141
149
|
| `--fdy-app-sidebar-w` | overriding `width` on `.fdy-app__sidebar`, which is also the rule that animates the collapse |
|
|
142
150
|
| `@media (--fdy-filterbar-stacked)` / `(--fdy-nav-drawer)`, from `@cahyo-dimas/freeday/media` | a literal `640px` or `720px` retyped into your stylesheet, which no longer matches the kit's rule the day the kit retunes it — and which you could only have found by reading `filterbar.css` |
|
package/docs/getting-started.md
CHANGED
|
@@ -191,7 +191,7 @@ live docs also have a copy button per component.
|
|
|
191
191
|
```bash
|
|
192
192
|
npm i @cahyo-dimas/freeday
|
|
193
193
|
```
|
|
194
|
-
Lands in `package.json` as `"@cahyo-dimas/freeday": "^3.
|
|
194
|
+
Lands in `package.json` as `"@cahyo-dimas/freeday": "^3.7.0"` (public npm package). `dist/` is
|
|
195
195
|
committed and published → no build step; `npm ci` runs without auth.
|
|
196
196
|
|
|
197
197
|
### 2. Import the CSS + enhancers **once** in your entry (`src/main.ts`)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cahyo-dimas/freeday",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.0",
|
|
4
4
|
"description": "Freeday: token-driven, framework-agnostic UI KIT (design source-of-truth).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"scripts": {
|
|
97
97
|
"build": "node tokens/build.mjs",
|
|
98
98
|
"test": "node --test",
|
|
99
|
-
"test:browser": "node --test --test-concurrency=3 browser/vanilla.mjs browser/adapter.mjs browser/layout.mjs browser/theme.mjs browser/state.mjs browser/root-init.mjs browser/upload-states.mjs browser/number.mjs browser/card-stretch.mjs browser/text-override.mjs browser/control-heights.mjs browser/field-width.mjs browser/cfl-multi.mjs browser/cfl-dialog-only.mjs browser/crowding.mjs browser/over-dialog.mjs browser/chart-scale.mjs browser/overlay-stack.mjs browser/chart-a11y.mjs browser/app-shell.mjs browser/bottom-nav.mjs browser/picker-states.mjs browser/tabs-strip.mjs browser/cfl-narrow.mjs browser/harness-contract.mjs",
|
|
99
|
+
"test:browser": "node --test --test-concurrency=3 browser/color-mix.mjs browser/vanilla.mjs browser/adapter.mjs browser/layout.mjs browser/theme.mjs browser/state.mjs browser/root-init.mjs browser/upload-states.mjs browser/number.mjs browser/card-stretch.mjs browser/text-override.mjs browser/control-heights.mjs browser/field-width.mjs browser/cfl-multi.mjs browser/cfl-dialog-only.mjs browser/crowding.mjs browser/over-dialog.mjs browser/chart-scale.mjs browser/overlay-stack.mjs browser/chart-a11y.mjs browser/app-shell.mjs browser/bottom-nav.mjs browser/picker-states.mjs browser/tabs-strip.mjs browser/cfl-narrow.mjs browser/form-reveal.mjs browser/confirm-search-copy.mjs browser/daterange-settled.mjs browser/display-parts.mjs browser/table-empty.mjs browser/page-columns.mjs browser/table-hover.mjs browser/harness-contract.mjs",
|
|
100
100
|
"prepack": "node tokens/build.mjs",
|
|
101
101
|
"version": "node tokens/build.mjs && git add dist",
|
|
102
102
|
"typecheck:react": "tsc -p adapters/react/tsconfig.json --noEmit",
|
package/src/base.css
CHANGED
|
@@ -11,7 +11,7 @@ body {
|
|
|
11
11
|
-webkit-font-smoothing: antialiased;
|
|
12
12
|
}
|
|
13
13
|
h1, h2, h3, h4 { font-family: var(--font-display); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tighter); line-height: var(--leading-snug); margin: 0; }
|
|
14
|
-
a { color: var(--color-primary); }
|
|
14
|
+
a { color: var(--color-primary-strong); }
|
|
15
15
|
:focus-visible { outline: var(--focus-ring-width) solid var(--focus-ring); outline-offset: var(--focus-ring-width); border-radius: var(--radius-xs); }
|
|
16
16
|
:where(button, input, select, textarea) { font: inherit; }
|
|
17
17
|
/* Hidden from sight, kept for assistive tech. `clip` hides PAINTING, not LAYOUT: the box still
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
.fdy-accordion__item > summary::after{content:"";margin-left:auto;margin-top:-.15rem;flex:none;width:.5rem;height:.5rem;border-right:2px solid var(--color-text-subtle);border-bottom:2px solid var(--color-text-subtle);transform:rotate(45deg);transition:transform var(--dur-fast) var(--ease-standard);}
|
|
11
11
|
.fdy-accordion__item[open] > summary::after{transform:rotate(225deg);margin-top:.1rem;}
|
|
12
12
|
.fdy-accordion__item > summary:hover{background:var(--color-surface-2);}
|
|
13
|
-
.fdy-accordion__item > summary:focus-visible{outline:none;box-shadow:inset 0 0 0 2px
|
|
13
|
+
.fdy-accordion__item > summary:focus-visible{outline:none;box-shadow:inset 0 0 0 2px rgba(var(--color-primary-rgb),.45);}
|
|
14
14
|
.fdy-accordion__panel{padding:0 var(--space-4) var(--space-4);color:var(--color-text-muted);font-size:var(--text-sm);line-height:var(--leading-normal);}
|
|
15
15
|
/* Content reveal on open (fade + rise). Native <details> can't animate its own height cross-
|
|
16
16
|
* browser without JS, so we animate the revealed panel instead, cheap, safe, reduced-motion gated. */
|
package/src/components/alert.css
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
.fdy-alert__text{color:var(--color-text);line-height:var(--leading-normal);}
|
|
8
8
|
.fdy-alert__close{flex:none;align-self:flex-start;border:0;background:transparent;color:var(--color-text-muted);cursor:pointer;font-size:var(--text-lg);line-height:1;padding:0 var(--space-1);border-radius:var(--radius-sm);}
|
|
9
9
|
.fdy-alert__close:hover{color:var(--color-text);}
|
|
10
|
-
.fdy-alert__close:focus-visible{outline:none;box-shadow:0 0 0 3px
|
|
11
|
-
.fdy-alert--info{background:var(--color-info-soft);border-color:
|
|
10
|
+
.fdy-alert__close:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
|
|
11
|
+
.fdy-alert--info{background:var(--color-info-soft);border-color:rgba(var(--color-info-rgb),.26);}
|
|
12
12
|
.fdy-alert--info .fdy-alert__title,.fdy-alert--info .fdy-alert__icon{color:var(--color-info-strong);}
|
|
13
|
-
.fdy-alert--success{background:var(--color-success-soft);border-color:
|
|
13
|
+
.fdy-alert--success{background:var(--color-success-soft);border-color:rgba(var(--color-success-rgb),.26);}
|
|
14
14
|
.fdy-alert--success .fdy-alert__title,.fdy-alert--success .fdy-alert__icon{color:var(--color-success-strong);}
|
|
15
|
-
.fdy-alert--warning{background:var(--color-warning-soft);border-color:
|
|
15
|
+
.fdy-alert--warning{background:var(--color-warning-soft);border-color:rgba(var(--color-warning-rgb),.26);}
|
|
16
16
|
.fdy-alert--warning .fdy-alert__title,.fdy-alert--warning .fdy-alert__icon{color:var(--color-warning-strong);}
|
|
17
|
-
.fdy-alert--danger{background:var(--color-danger-soft);border-color:
|
|
17
|
+
.fdy-alert--danger{background:var(--color-danger-soft);border-color:rgba(var(--color-danger-rgb),.26);}
|
|
18
18
|
.fdy-alert--danger .fdy-alert__title,.fdy-alert--danger .fdy-alert__icon{color:var(--color-danger-strong);}
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
.fdy-nav__item:hover{background:var(--color-surface-2);color:var(--color-text);}
|
|
45
45
|
/* Active indicator stays in the primary ramp so it never clashes with a re-themed accent. */
|
|
46
46
|
.fdy-nav__item[aria-current="page"]{background:var(--color-primary-soft);color:var(--color-primary-strong);font-weight:var(--weight-semibold);}
|
|
47
|
-
.fdy-nav__item:focus-visible{outline:none;box-shadow:0 0 0 2px
|
|
47
|
+
.fdy-nav__item:focus-visible{outline:none;box-shadow:0 0 0 2px rgba(var(--color-primary-rgb),.45);}
|
|
48
48
|
/* Sidebar-menu extras: per-node icon (leading), count badge (trailing), and a
|
|
49
49
|
* text label that truncates. Any of these are optional, a bare text item works. */
|
|
50
50
|
.fdy-nav__icon{flex:none;display:inline-flex;width:1.15rem;height:1.15rem;}
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
.fdy-nav__grouplabel{list-style:none;cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:var(--space-2);padding:var(--space-3) var(--space-3) var(--space-1);font-size:var(--text-xs);text-transform:uppercase;letter-spacing:var(--tracking-wide);font-weight:var(--weight-semibold);color:var(--color-text-muted);}
|
|
63
63
|
.fdy-nav__grouplabel::-webkit-details-marker{display:none;}
|
|
64
64
|
.fdy-nav__grouplabel:hover{color:var(--color-text-muted);}
|
|
65
|
-
.fdy-nav__grouplabel:focus-visible{outline:none;border-radius:var(--radius-sm);box-shadow:0 0 0 2px
|
|
65
|
+
.fdy-nav__grouplabel:focus-visible{outline:none;border-radius:var(--radius-sm);box-shadow:0 0 0 2px rgba(var(--color-primary-rgb),.45);}
|
|
66
66
|
.fdy-nav__grouplabel::after{content:"";flex:none;width:.45rem;height:.45rem;margin-right:var(--space-1);border-right:2px solid currentColor;border-bottom:2px solid currentColor;transform:rotate(-45deg);transition:transform var(--dur-fast) var(--ease-standard);opacity:.7;}
|
|
67
67
|
.fdy-nav__group[open] > .fdy-nav__grouplabel::after{transform:rotate(45deg);}
|
|
68
68
|
/* --flat: calmer, non-collapsible grouped nav, hide the caret, drop the
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
is the same component whether the nav is a sidebar column or a top row. */
|
|
16
16
|
/* Controls on a coloured app bar go on-colour (icons stay legible, hover is a light wash) */
|
|
17
17
|
.fdy-appbar--primary .fdy-btn--ghost{background:transparent;border-color:transparent;color:var(--color-on-primary);box-shadow:none;}
|
|
18
|
-
.fdy-appbar--primary .fdy-btn--ghost:hover{background:
|
|
18
|
+
.fdy-appbar--primary .fdy-btn--ghost:hover{background:rgba(var(--color-on-primary-rgb),.16);color:var(--color-on-primary);transform:none;}
|
|
19
19
|
/* Nav links on a coloured bar: full on-colour ink in every state (a dimmed variant would trade away
|
|
20
20
|
the contrast the bar's own token pair guarantees). The current page is carried by the background
|
|
21
21
|
wash plus the semibold weight the base rule already applies, and by `aria-current` itself. */
|
|
22
22
|
.fdy-appbar--primary .fdy-nav__item{color:var(--color-on-primary);}
|
|
23
|
-
.fdy-appbar--primary .fdy-nav__item:hover{background:
|
|
24
|
-
.fdy-appbar--primary .fdy-nav__item[aria-current="page"]{background:
|
|
23
|
+
.fdy-appbar--primary .fdy-nav__item:hover{background:rgba(var(--color-on-primary-rgb),.16);color:var(--color-on-primary);}
|
|
24
|
+
.fdy-appbar--primary .fdy-nav__item[aria-current="page"]{background:rgba(var(--color-on-primary-rgb),.24);color:var(--color-on-primary);}
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
.fdy-avatar--xs{width:1.5rem;height:1.5rem;font-size:var(--text-xs);}
|
|
9
9
|
.fdy-avatar--sm{width:2rem;height:2rem;font-size:var(--text-xs);}
|
|
10
10
|
.fdy-avatar--lg{width:3.5rem;height:3.5rem;font-size:var(--text-lg);}
|
|
11
|
+
/* Square: a mark that is not a person (a company logo, a card's icon chip, #070). */
|
|
12
|
+
.fdy-avatar--square{border-radius:var(--radius-md);}
|
|
11
13
|
.fdy-avatar-group{display:inline-flex;}
|
|
12
14
|
.fdy-avatar-group .fdy-avatar{border:2px solid var(--color-surface);margin-left:calc(var(--space-2)*-1);}
|
|
13
15
|
.fdy-avatar-group .fdy-avatar:first-child{margin-left:0;}
|
|
@@ -15,12 +17,18 @@
|
|
|
15
17
|
* text-leaning foreground so initials keep WCAG AA (verified >=4.5:1 in light & dark by
|
|
16
18
|
* test/contrast.test.mjs). Use to disambiguate same-initial avatars (e.g. many "PT …" names):
|
|
17
19
|
* hash a stable 1..8 index off the FULL name in your app, then apply .fdy-avatar--tone-<n>. */
|
|
18
|
-
.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
.
|
|
24
|
-
.
|
|
25
|
-
.fdy-avatar--tone-
|
|
26
|
-
.fdy-avatar--tone-
|
|
20
|
+
/* One flat token per half, computed in advance by tokens/build.mjs: --tone-N-bg is 18% of the
|
|
21
|
+
* tone over --color-surface, --tone-N-ink is 50% of it toward --color-text, both per theme.
|
|
22
|
+
* They USED to be mixed here, with the hue injected per element through a private custom
|
|
23
|
+
* property — which put var() inside color-mix(), the one shape that cannot be rescued: valid at
|
|
24
|
+
* parse time, invalid at computed-value time, and there `background` and `color` drop to their
|
|
25
|
+
* INITIAL values, not to any fallback. @supports answered it for exactly as long as somebody
|
|
26
|
+
* remembered to write the counterpart; a precomputed token removes the question. */
|
|
27
|
+
.fdy-avatar--tone-1{background:var(--tone-1-bg);color:var(--tone-1-ink);}
|
|
28
|
+
.fdy-avatar--tone-2{background:var(--tone-2-bg);color:var(--tone-2-ink);}
|
|
29
|
+
.fdy-avatar--tone-3{background:var(--tone-3-bg);color:var(--tone-3-ink);}
|
|
30
|
+
.fdy-avatar--tone-4{background:var(--tone-4-bg);color:var(--tone-4-ink);}
|
|
31
|
+
.fdy-avatar--tone-5{background:var(--tone-5-bg);color:var(--tone-5-ink);}
|
|
32
|
+
.fdy-avatar--tone-6{background:var(--tone-6-bg);color:var(--tone-6-ink);}
|
|
33
|
+
.fdy-avatar--tone-7{background:var(--tone-7-bg);color:var(--tone-7-ink);}
|
|
34
|
+
.fdy-avatar--tone-8{background:var(--tone-8-bg);color:var(--tone-8-ink);}
|
package/src/components/badge.css
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/* Freeday. Badge */
|
|
2
|
-
.fdy-badge{display:inline-flex;align-items:center;gap:var(--space-1);font-family:var(--font-body);font-size:var(--text-xs);font-weight:var(--weight-semibold);line-height:1;white-space:nowrap;padding:.35em .7em;border-radius:var(--radius-full);color:var(--color-primary-strong);background:var(--color-primary-soft);border:var(--bw) solid
|
|
3
|
-
.fdy-badge--success{color:var(--color-success-strong);background:var(--color-success-soft);border-color:
|
|
4
|
-
.fdy-badge--warning{color:var(--color-warning-strong);background:var(--color-warning-soft);border-color:
|
|
5
|
-
.fdy-badge--danger{color:var(--color-danger-strong);background:var(--color-danger-soft);border-color:
|
|
6
|
-
.fdy-badge--info{color:var(--color-info-strong);background:var(--color-info-soft);border-color:
|
|
2
|
+
.fdy-badge{display:inline-flex;align-items:center;gap:var(--space-1);font-family:var(--font-body);font-size:var(--text-xs);font-weight:var(--weight-semibold);line-height:1;white-space:nowrap;padding:.35em .7em;border-radius:var(--radius-full);color:var(--color-primary-strong);background:var(--color-primary-soft);border:var(--bw) solid rgba(var(--color-primary-rgb),.22);box-shadow:0 1px 1.5px rgba(16,14,30,.05);}
|
|
3
|
+
.fdy-badge--success{color:var(--color-success-strong);background:var(--color-success-soft);border-color:rgba(var(--color-success-rgb),.26);}
|
|
4
|
+
.fdy-badge--warning{color:var(--color-warning-strong);background:var(--color-warning-soft);border-color:rgba(var(--color-warning-rgb),.26);}
|
|
5
|
+
.fdy-badge--danger{color:var(--color-danger-strong);background:var(--color-danger-soft);border-color:rgba(var(--color-danger-rgb),.26);}
|
|
6
|
+
.fdy-badge--info{color:var(--color-info-strong);background:var(--color-info-soft);border-color:rgba(var(--color-info-rgb),.26);}
|
|
7
7
|
.fdy-badge--outline{background:transparent;border-color:var(--color-border-strong);color:var(--color-text-muted);box-shadow:none;}
|
|
8
8
|
|
|
9
9
|
/* Categorical tone badges, a status vocabulary larger than the semantic palette, from the
|
|
@@ -15,15 +15,19 @@
|
|
|
15
15
|
* cannot. A back-office document list carrying Draft, Submitted, Approved, Completed, Settled,
|
|
16
16
|
* Closed, Transferred, InDeclaration, Open and Rejected in ONE column collapsed to three colours,
|
|
17
17
|
* which is the report this shipped for. */
|
|
18
|
-
|
|
19
|
-
.
|
|
20
|
-
|
|
21
|
-
.
|
|
22
|
-
.
|
|
23
|
-
.fdy-badge--tone-
|
|
24
|
-
.fdy-badge--tone-
|
|
25
|
-
.fdy-badge--tone-
|
|
26
|
-
.fdy-badge--tone-
|
|
18
|
+
/* Precomputed tone tokens, the same --tone-N-bg / --tone-N-ink the avatar and the chip read, so
|
|
19
|
+
* one contrast assertion still covers all three. --tone-N-border-soft is the badge's own edge:
|
|
20
|
+
* 26% of the tone over transparent, which is a translucent colour and therefore stays rgba()
|
|
21
|
+
* rather than flattening to a hex. See avatar.css for why none of this is mixed in the
|
|
22
|
+
* component any more, and tokens/build.mjs for where the mixing moved to. */
|
|
23
|
+
.fdy-badge--tone-1{background:var(--tone-1-bg);color:var(--tone-1-ink);border-color:var(--tone-1-border-soft);}
|
|
24
|
+
.fdy-badge--tone-2{background:var(--tone-2-bg);color:var(--tone-2-ink);border-color:var(--tone-2-border-soft);}
|
|
25
|
+
.fdy-badge--tone-3{background:var(--tone-3-bg);color:var(--tone-3-ink);border-color:var(--tone-3-border-soft);}
|
|
26
|
+
.fdy-badge--tone-4{background:var(--tone-4-bg);color:var(--tone-4-ink);border-color:var(--tone-4-border-soft);}
|
|
27
|
+
.fdy-badge--tone-5{background:var(--tone-5-bg);color:var(--tone-5-ink);border-color:var(--tone-5-border-soft);}
|
|
28
|
+
.fdy-badge--tone-6{background:var(--tone-6-bg);color:var(--tone-6-ink);border-color:var(--tone-6-border-soft);}
|
|
29
|
+
.fdy-badge--tone-7{background:var(--tone-7-bg);color:var(--tone-7-ink);border-color:var(--tone-7-border-soft);}
|
|
30
|
+
.fdy-badge--tone-8{background:var(--tone-8-bg);color:var(--tone-8-ink);border-color:var(--tone-8-border-soft);}
|
|
27
31
|
|
|
28
32
|
/* Overlay badge, anchors a small count/dot/icon badge to a corner of any element
|
|
29
33
|
* (icon, button, text). Wrap the content in .fdy-badge-wrap; add .fdy-badge-ov as a
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
.fdy-breadcrumb__item{display:inline-flex;align-items:center;gap:var(--space-2);color:var(--color-text-muted);}
|
|
5
5
|
.fdy-breadcrumb__link{color:var(--color-text-muted);text-decoration:none;border-radius:var(--radius-sm);}
|
|
6
6
|
.fdy-breadcrumb__link:hover{color:var(--color-primary-strong);text-decoration:underline;}
|
|
7
|
-
.fdy-breadcrumb__link:focus-visible{outline:none;box-shadow:0 0 0 3px
|
|
7
|
+
.fdy-breadcrumb__link:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
|
|
8
8
|
.fdy-breadcrumb__item[aria-current="page"]{color:var(--color-text);font-weight:var(--weight-medium);}
|
|
9
9
|
.fdy-breadcrumb__sep{color:var(--color-text-subtle);user-select:none;}
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
/* Freeday. Button */
|
|
2
|
+
/* The fill is a FLAT colour with a black veil laid over it, not a gradient between two shades.
|
|
3
|
+
* It used to be `linear-gradient(primary, color-mix(in srgb, #000 10%, primary))`, and that is the
|
|
4
|
+
* same colour at every stop — veiling a solid base with black 0 -> .1 darkens it by exactly 10% —
|
|
5
|
+
* but the mix form carries var(), so on Safari <= 16.1 the whole declaration failed at
|
|
6
|
+
* computed-value time and the button lost its fill rather than falling back. Measured identical to
|
|
7
|
+
* the mix it replaces (within the 1/255 the gradient's own 8-bit rounding costs); see
|
|
8
|
+
* test/color-mix.test.mjs for the rule and tokens/build.mjs for why none of these may use
|
|
9
|
+
* color-mix(). Pressed states veil from the TOP, which is the same trick read upside down. */
|
|
2
10
|
.fdy-btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);
|
|
3
11
|
height:var(--control-h);padding:0 var(--space-4);
|
|
4
12
|
font-family:var(--font-body);font-size:var(--text-sm);font-weight:var(--weight-semibold);
|
|
5
13
|
line-height:1;white-space:nowrap;cursor:pointer;text-decoration:none;color:var(--color-on-primary);
|
|
6
|
-
background:
|
|
14
|
+
background:var(--color-primary) linear-gradient(180deg,rgba(0,0,0,0),rgba(0,0,0,.1));
|
|
7
15
|
border:var(--bw) solid transparent;border-radius:var(--radius-md);
|
|
8
|
-
box-shadow:0 2px 9px -1px
|
|
16
|
+
box-shadow:0 2px 9px -1px rgba(var(--color-primary-rgb),.5),inset 0 1px 0 rgba(255,255,255,.26);
|
|
9
17
|
transition:background var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard),transform var(--dur-fast) var(--ease-standard);}
|
|
10
|
-
.fdy-btn:hover{transform:translateY(-1px);box-shadow:0 6px 18px -2px
|
|
11
|
-
.fdy-btn:active{transform:translateY(1px);box-shadow:0 1px 4px -1px
|
|
18
|
+
.fdy-btn:hover{transform:translateY(-1px);box-shadow:0 6px 18px -2px rgba(var(--color-primary-rgb),.6),inset 0 1px 0 rgba(255,255,255,.26);}
|
|
19
|
+
.fdy-btn:active{transform:translateY(1px);box-shadow:0 1px 4px -1px rgba(var(--color-primary-rgb),.46);}
|
|
12
20
|
.fdy-btn:disabled{opacity:.5;cursor:not-allowed;transform:none;box-shadow:none;}
|
|
13
21
|
/* Pressed / toggle. `aria-pressed` is already the right attribute for a toggle button, so this adds
|
|
14
22
|
no class and no new markup contract, and it is what makes .fdy-btn-group a complete segmented
|
|
@@ -16,8 +24,8 @@
|
|
|
16
24
|
defines its OWN pressed look below; a single shared rule cannot work, because a gradient is a
|
|
17
25
|
background-IMAGE and would blank the ghost variant's background-color. Solid: invert the gradient
|
|
18
26
|
and sink the shadow inward, so a held button reads as depressed, not merely primary-coloured. */
|
|
19
|
-
.fdy-btn[aria-pressed="true"]{background:linear-gradient(180deg,
|
|
20
|
-
.fdy-btn[aria-pressed="true"]:hover{transform:none;box-shadow:inset 0 2px 6px
|
|
27
|
+
.fdy-btn[aria-pressed="true"]{background:var(--color-primary) linear-gradient(180deg,rgba(0,0,0,.12),rgba(0,0,0,0));box-shadow:inset 0 2px 6px rgba(0,0,0,.22);}
|
|
28
|
+
.fdy-btn[aria-pressed="true"]:hover{transform:none;box-shadow:inset 0 2px 6px rgba(0,0,0,.22);}
|
|
21
29
|
|
|
22
30
|
.fdy-btn--ghost{background:var(--color-surface);color:var(--color-primary-strong);border-color:var(--color-border-strong);box-shadow:var(--shadow-1);}
|
|
23
31
|
.fdy-btn--ghost:hover{transform:none;background:var(--color-primary-soft);border-color:var(--color-primary-border);box-shadow:var(--shadow-1);}
|
|
@@ -29,15 +37,15 @@
|
|
|
29
37
|
screen. `.fdy-menu__item--danger` has always modelled the quiet form (red text, not a red block);
|
|
30
38
|
the pairing rules below bring it to the button. They are more specific than this one and come
|
|
31
39
|
after it, so this rule needs no :not() guard of its own, one mechanism, not two. */
|
|
32
|
-
.fdy-btn--danger{background:
|
|
33
|
-
.fdy-btn--danger:hover{transform:translateY(-1px);box-shadow:0 6px 18px -2px
|
|
40
|
+
.fdy-btn--danger{background:var(--color-danger-btn) linear-gradient(180deg,rgba(0,0,0,0),rgba(0,0,0,.12));color:var(--color-on-danger);box-shadow:0 2px 9px -1px rgba(var(--color-danger-btn-rgb),.46),inset 0 1px 0 rgba(255,255,255,.22);}
|
|
41
|
+
.fdy-btn--danger:hover{transform:translateY(-1px);box-shadow:0 6px 18px -2px rgba(var(--color-danger-btn-rgb),.54),inset 0 1px 0 rgba(255,255,255,.22);}
|
|
34
42
|
.fdy-btn--danger:active{transform:translateY(1px);}
|
|
35
43
|
/* Quiet destructive: the variant keeps its own ground and only the ink turns. */
|
|
36
|
-
.fdy-btn--ghost.fdy-btn--danger{background:transparent;color:var(--color-danger-strong);border-color:
|
|
44
|
+
.fdy-btn--ghost.fdy-btn--danger{background:transparent;color:var(--color-danger-strong);border-color:rgba(var(--color-danger-rgb),.4);box-shadow:none;}
|
|
37
45
|
.fdy-btn--ghost.fdy-btn--danger:hover{background:var(--color-danger-soft);border-color:var(--color-danger);color:var(--color-danger-strong);}
|
|
38
46
|
.fdy-btn--text.fdy-btn--danger{background:none;color:var(--color-danger-strong);border-color:transparent;box-shadow:none;}
|
|
39
47
|
.fdy-btn--text.fdy-btn--danger:hover{background:var(--color-danger-soft);color:var(--color-danger-strong);}
|
|
40
|
-
.fdy-btn--danger[aria-pressed="true"]{background:
|
|
48
|
+
.fdy-btn--danger[aria-pressed="true"]{background:var(--color-danger-btn) linear-gradient(180deg,rgba(0,0,0,.14),rgba(0,0,0,0));box-shadow:inset 0 2px 6px rgba(0,0,0,.24);}
|
|
41
49
|
|
|
42
50
|
.fdy-btn--sm{height:calc(var(--control-h) - var(--space-2));padding:0 var(--space-3);font-size:var(--text-xs);border-radius:var(--radius-sm);}
|
|
43
51
|
.fdy-btn--lg{height:calc(var(--control-h) + var(--space-2));padding:0 var(--space-6);font-size:var(--text-base);border-radius:var(--radius-lg);}
|
|
@@ -86,7 +94,7 @@
|
|
|
86
94
|
.fdy-btn-group .fdy-btn:last-child{border-top-right-radius:var(--radius-md);border-bottom-right-radius:var(--radius-md);}
|
|
87
95
|
|
|
88
96
|
/* FAB, circular elevated action button (icon), optional extended pill with label */
|
|
89
|
-
.fdy-fab{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);width:3.5rem;height:3.5rem;padding:0;border:var(--bw) solid transparent;border-radius:var(--radius-full);cursor:pointer;color:var(--color-on-primary);background:
|
|
97
|
+
.fdy-fab{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);width:3.5rem;height:3.5rem;padding:0;border:var(--bw) solid transparent;border-radius:var(--radius-full);cursor:pointer;color:var(--color-on-primary);background:var(--color-primary) linear-gradient(180deg,rgba(0,0,0,0),rgba(0,0,0,.1));box-shadow:var(--shadow-3);transition:transform var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
|
|
90
98
|
.fdy-fab:hover{transform:translateY(-1px);box-shadow:var(--shadow-4);}
|
|
91
99
|
.fdy-fab:active{transform:translateY(1px);}
|
|
92
100
|
.fdy-fab:focus-visible{border-radius:var(--radius-full);}
|
|
@@ -95,5 +103,5 @@
|
|
|
95
103
|
.fdy-fab--sm{width:2.5rem;height:2.5rem;}
|
|
96
104
|
.fdy-fab--sm svg{width:1.25rem;height:1.25rem;}
|
|
97
105
|
.fdy-fab--extended{width:auto;padding:0 var(--space-5);font-family:var(--font-body);font-size:var(--text-sm);font-weight:var(--weight-semibold);}
|
|
98
|
-
.fdy-fab--danger{background:
|
|
99
|
-
.fdy-fab--accent{background:
|
|
106
|
+
.fdy-fab--danger{background:var(--color-danger-btn) linear-gradient(180deg,rgba(0,0,0,0),rgba(0,0,0,.12));color:var(--color-on-danger);}
|
|
107
|
+
.fdy-fab--accent{background:var(--color-accent) linear-gradient(180deg,rgba(0,0,0,0),rgba(0,0,0,.1));color:var(--color-on-accent);}
|
package/src/components/card.css
CHANGED
|
@@ -32,6 +32,13 @@
|
|
|
32
32
|
absolutely positioned dismiss button back into the flow. Do not merge these two rules. */
|
|
33
33
|
:where(:is(.fdy-card,.fdy-list__row):has(.fdy-btn--stretch) :is(a[href],button,input,select,textarea,[tabindex]):not(.fdy-btn--stretch)){position:relative;}
|
|
34
34
|
.fdy-card__body{padding:var(--space-5);}
|
|
35
|
+
/* Header band (#070): an identity mark, the title, and the card's way out pinned right. Beside the
|
|
36
|
+
title the action sits in one place on every card; under the content it moved each time the
|
|
37
|
+
content grew. Outside __body's padding, so its rule spans the card edge to edge. The mark is an
|
|
38
|
+
.fdy-avatar (--sm --square --tone-N), so its tints are the ones the contrast gate already covers. */
|
|
39
|
+
.fdy-card__head{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-4) var(--space-5);border-bottom:var(--bw) solid var(--color-border-muted);}
|
|
40
|
+
.fdy-card__head .fdy-card__title{flex:1 1 auto;min-width:0;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
41
|
+
.fdy-card__action{flex:none;display:inline-flex;align-items:center;gap:var(--space-2);}
|
|
35
42
|
.fdy-card__title{font-family:var(--font-display);font-size:var(--text-lg);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-tight);color:var(--color-text);margin:0 0 var(--space-1);}
|
|
36
43
|
.fdy-card__desc{font-size:var(--text-sm);color:var(--color-text-muted);margin:0;line-height:var(--leading-normal);}
|
|
37
44
|
.fdy-card__footer{padding:var(--space-3) var(--space-5);border-top:var(--bw) solid var(--color-border-muted);background:linear-gradient(180deg,var(--color-surface),var(--color-surface-2));display:flex;gap:var(--space-2);justify-content:flex-end;}
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
.fdy-carousel__viewport{position:relative;display:flex;overflow-x:auto;scroll-snap-type:x mandatory;scroll-behavior:smooth;border-radius:var(--radius-lg);scrollbar-width:none;-ms-overflow-style:none;}
|
|
7
7
|
.fdy-carousel__viewport::-webkit-scrollbar{display:none;}
|
|
8
8
|
.fdy-carousel__slide{flex:0 0 100%;min-width:0;scroll-snap-align:center;}
|
|
9
|
-
.fdy-carousel__arrow{position:absolute;top:50%;transform:translateY(-50%);z-index:2;display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border:0;border-radius:var(--radius-full);background:
|
|
9
|
+
.fdy-carousel__arrow{position:absolute;top:50%;transform:translateY(-50%);z-index:2;display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border:0;border-radius:var(--radius-full);background:rgba(var(--color-surface-rgb),.86);color:var(--color-text);box-shadow:var(--shadow-2);cursor:pointer;transition:background var(--dur-fast) var(--ease-standard);}
|
|
10
10
|
.fdy-carousel__arrow:hover{background:var(--color-surface);}
|
|
11
|
-
.fdy-carousel__arrow:focus-visible{outline:none;box-shadow:0 0 0 3px
|
|
11
|
+
.fdy-carousel__arrow:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.4);}
|
|
12
12
|
.fdy-carousel__arrow:disabled{opacity:.4;cursor:not-allowed;}
|
|
13
13
|
.fdy-carousel__arrow--prev{left:var(--space-3);}
|
|
14
14
|
.fdy-carousel__arrow--next{right:var(--space-3);}
|
|
@@ -16,5 +16,5 @@
|
|
|
16
16
|
.fdy-carousel__dots{display:flex;justify-content:center;gap:var(--space-2);margin-top:var(--space-3);}
|
|
17
17
|
.fdy-carousel__dot{width:.5rem;height:.5rem;padding:0;border:0;border-radius:var(--radius-full);background:var(--color-border-strong);cursor:pointer;transition:background var(--dur-fast) var(--ease-standard),width var(--dur-fast) var(--ease-standard);}
|
|
18
18
|
.fdy-carousel__dot[aria-current="true"]{background:var(--color-primary);width:1.25rem;}
|
|
19
|
-
.fdy-carousel__dot:focus-visible{outline:none;box-shadow:0 0 0 3px
|
|
19
|
+
.fdy-carousel__dot:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.4);}
|
|
20
20
|
@media (prefers-reduced-motion:reduce){.fdy-carousel__viewport{scroll-behavior:auto;}}
|
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
.fdy-cascade{position:relative;display:inline-block;width:100%;max-width:22rem;}
|
|
8
8
|
.fdy-cascade__trigger{display:inline-flex;align-items:center;justify-content:space-between;gap:var(--space-2);width:100%;height:var(--control-h);padding:0 var(--space-3);text-align:left;cursor:pointer;font-family:var(--font-body);font-size:var(--text-sm);color:var(--color-text);background:var(--color-surface);border:1.5px solid var(--color-control-border);border-radius:var(--radius-md);box-shadow:inset 0 1px 2px rgba(16,14,30,.06);transition:border-color var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
|
|
9
9
|
.fdy-cascade__trigger:hover{border-color:var(--color-text-muted);}
|
|
10
|
-
.fdy-cascade__trigger:focus-visible{outline:none;border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px
|
|
11
|
-
.fdy-cascade__trigger.is-open{border-color:var(--color-primary);box-shadow:0 0 0 3px
|
|
10
|
+
.fdy-cascade__trigger:focus-visible{outline:none;border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px rgba(var(--color-primary-rgb),.26);}
|
|
11
|
+
.fdy-cascade__trigger.is-open{border-color:var(--color-primary);box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
|
|
12
12
|
.fdy-cascade__trigger:disabled,.fdy-cascade__trigger[aria-disabled="true"]{opacity:.5;cursor:not-allowed;}
|
|
13
13
|
.fdy-cascade__trigger[aria-readonly="true"]{background:var(--color-surface-2);border-color:var(--color-border);cursor:default;}
|
|
14
14
|
.fdy-cascade__trigger[aria-readonly="true"]:hover{border-color:var(--color-border);}
|
|
15
15
|
.fdy-cascade__trigger[aria-readonly="true"]:focus-visible{border-color:var(--color-border-strong);box-shadow:none;}
|
|
16
16
|
.fdy-cascade--error .fdy-cascade__trigger,.fdy-cascade__trigger[aria-invalid="true"]{border-color:var(--color-danger);}
|
|
17
|
-
.fdy-cascade--error .fdy-cascade__trigger:focus-visible,.fdy-cascade--error .fdy-cascade__trigger.is-open,.fdy-cascade__trigger[aria-invalid="true"]:focus-visible{border-color:var(--color-danger);box-shadow:0 0 0 3px
|
|
17
|
+
.fdy-cascade--error .fdy-cascade__trigger:focus-visible,.fdy-cascade--error .fdy-cascade__trigger.is-open,.fdy-cascade__trigger[aria-invalid="true"]:focus-visible{border-color:var(--color-danger);box-shadow:0 0 0 3px rgba(var(--color-danger-rgb),.26);}
|
|
18
18
|
.fdy-cascade__value{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
19
19
|
.fdy-cascade__value--placeholder{color:var(--color-text-subtle);}
|
|
20
20
|
.fdy-cascade__trigger::after{content:"";flex:none;width:.5rem;height:.5rem;margin-top:-.15rem;border-right:2px solid var(--color-text-subtle);border-bottom:2px solid var(--color-text-subtle);transform:rotate(45deg);transition:transform var(--dur-fast) var(--ease-standard);}
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
.fdy-cascade__head{display:flex;align-items:center;gap:var(--space-2);padding:var(--space-1) var(--space-1) var(--space-2);margin-bottom:var(--space-1);border-bottom:var(--bw) solid var(--color-border);}
|
|
27
27
|
.fdy-cascade__back{display:inline-flex;align-items:center;justify-content:center;flex:none;width:1.5rem;height:1.5rem;padding:0;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-text-muted);cursor:pointer;}
|
|
28
28
|
.fdy-cascade__back:hover{background:var(--color-surface-3);color:var(--color-text);}
|
|
29
|
-
.fdy-cascade__back:focus-visible{outline:none;box-shadow:0 0 0 2px
|
|
29
|
+
.fdy-cascade__back:focus-visible{outline:none;box-shadow:0 0 0 2px rgba(var(--color-primary-rgb),.45);}
|
|
30
30
|
.fdy-cascade__back[hidden]{display:none;}
|
|
31
31
|
.fdy-cascade__back svg{display:block;width:1rem;height:1rem;}
|
|
32
32
|
.fdy-cascade__crumb{min-width:0;flex:1;font-size:var(--text-xs);font-weight:var(--weight-semibold);color:var(--color-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
package/src/components/chip.css
CHANGED
|
@@ -1,29 +1,47 @@
|
|
|
1
1
|
/* Freeday. Chip (tag / removable filter) */
|
|
2
2
|
.fdy-chip{display:inline-flex;align-items:center;gap:var(--space-2);height:1.75rem;padding:0 var(--space-3);border-radius:var(--radius-full);font-size:var(--text-xs);font-weight:var(--weight-medium);color:var(--color-text);background:var(--color-surface-3);border:var(--bw) solid var(--color-border);}
|
|
3
|
-
.fdy-chip--primary{color:var(--color-primary-strong);background:var(--color-primary-soft);border-color:
|
|
3
|
+
.fdy-chip--primary{color:var(--color-primary-strong);background:var(--color-primary-soft);border-color:rgba(var(--color-primary-rgb),.22);}
|
|
4
4
|
/* Categorical tone chips, non-semantic category tags / legend swatches, from the general
|
|
5
5
|
* --tone-1..8 palette. Same validated AA formula as .fdy-avatar--tone-* (text >=4.5:1 both themes,
|
|
6
6
|
* gated by test/contrast.test.mjs). */
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
.
|
|
10
|
-
.
|
|
11
|
-
.fdy-chip--tone-
|
|
12
|
-
.fdy-chip--tone-
|
|
13
|
-
.fdy-chip--tone-
|
|
14
|
-
.fdy-chip--tone-
|
|
15
|
-
.fdy-chip--tone-
|
|
16
|
-
.fdy-
|
|
17
|
-
.fdy-
|
|
18
|
-
.fdy-
|
|
7
|
+
/* Precomputed tone tokens, the same --tone-N-bg / --tone-N-ink the avatar and the badge read.
|
|
8
|
+
* --tone-N-border is the chip's edge: 30% of the tone over --color-border, opaque, which is a
|
|
9
|
+
* different edge from the badge's translucent one and so a different token. See avatar.css for
|
|
10
|
+
* why the mix left the component, and tokens/build.mjs for where it went. */
|
|
11
|
+
.fdy-chip--tone-1{background:var(--tone-1-bg);color:var(--tone-1-ink);border-color:var(--tone-1-border);}
|
|
12
|
+
.fdy-chip--tone-2{background:var(--tone-2-bg);color:var(--tone-2-ink);border-color:var(--tone-2-border);}
|
|
13
|
+
.fdy-chip--tone-3{background:var(--tone-3-bg);color:var(--tone-3-ink);border-color:var(--tone-3-border);}
|
|
14
|
+
.fdy-chip--tone-4{background:var(--tone-4-bg);color:var(--tone-4-ink);border-color:var(--tone-4-border);}
|
|
15
|
+
.fdy-chip--tone-5{background:var(--tone-5-bg);color:var(--tone-5-ink);border-color:var(--tone-5-border);}
|
|
16
|
+
.fdy-chip--tone-6{background:var(--tone-6-bg);color:var(--tone-6-ink);border-color:var(--tone-6-border);}
|
|
17
|
+
.fdy-chip--tone-7{background:var(--tone-7-bg);color:var(--tone-7-ink);border-color:var(--tone-7-border);}
|
|
18
|
+
.fdy-chip--tone-8{background:var(--tone-8-bg);color:var(--tone-8-ink);border-color:var(--tone-8-border);}
|
|
19
|
+
.fdy-chip__remove{position:relative;display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;margin-right:calc(var(--space-1)*-1);border:0;padding:0;border-radius:var(--radius-full);background:transparent;color:currentColor;opacity:.6;cursor:pointer;font-size:var(--text-sm);line-height:1;}
|
|
20
|
+
/* The hover wash is 16% of the chip's OWN ink, and the chip's ink is whatever the variant set:
|
|
21
|
+
* --color-text, --color-primary-strong, --tone-N-ink. So no token can stand in for it, which is why
|
|
22
|
+
* this was the last color-mix() in the kit — and the only one whose two-line fallback actually
|
|
23
|
+
* worked, the mix carrying no var() and therefore being rejected at parse time.
|
|
24
|
+
*
|
|
25
|
+
* It is a VEIL now, the same trick the button gradients use (button.css): currentColor at .16 over
|
|
26
|
+
* the chip, which is algebraically the colour the mix produced — `F·.16 + (1-.16)·ground` either
|
|
27
|
+
* way, including at the glyph's antialiased edges, where the veil's colour equals the glyph's and so
|
|
28
|
+
* composites back to itself. No @supports, no fallback line, and nothing newer than Safari 4.
|
|
29
|
+
*
|
|
30
|
+
* Absolutely positioned, so it paints above the × as well as the background; `border-radius:inherit`
|
|
31
|
+
* keeps it inside the pill. No transition: the mix had none, and a veil that fades is a different
|
|
32
|
+
* component. */
|
|
33
|
+
.fdy-chip__remove::before{content:"";position:absolute;inset:0;border-radius:inherit;background:currentColor;opacity:0;}
|
|
34
|
+
.fdy-chip__remove:hover{opacity:1;}
|
|
35
|
+
.fdy-chip__remove:hover::before{opacity:.16;}
|
|
36
|
+
.fdy-chip__remove:focus-visible{outline:none;opacity:1;box-shadow:0 0 0 2px rgba(var(--color-primary-rgb),.45);}
|
|
19
37
|
/* Group wrapper for a row of chips (choice / filter). */
|
|
20
38
|
.fdy-chips{display:flex;flex-wrap:wrap;gap:var(--space-2);}
|
|
21
39
|
/* Interactive chips: choice (toggle) + filter (toggle with a check when on).
|
|
22
40
|
* Rendered as <button aria-pressed> and enhanced by freeday-chip.js. */
|
|
23
41
|
.fdy-chip--choice,.fdy-chip--filter{cursor:pointer;font-family:inherit;background:var(--color-surface);border-color:var(--color-border);transition:background var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard),color var(--dur-fast) var(--ease-standard);}
|
|
24
42
|
.fdy-chip--choice:hover,.fdy-chip--filter:hover{background:var(--color-surface-3);border-color:var(--color-text-subtle);}
|
|
25
|
-
.fdy-chip--choice:focus-visible,.fdy-chip--filter:focus-visible{outline:none;box-shadow:0 0 0 2px
|
|
26
|
-
.fdy-chip--choice[aria-pressed="true"],.fdy-chip--filter[aria-pressed="true"]{color:var(--color-primary-strong);background:var(--color-primary-soft);border-color:
|
|
43
|
+
.fdy-chip--choice:focus-visible,.fdy-chip--filter:focus-visible{outline:none;box-shadow:0 0 0 2px rgba(var(--color-primary-rgb),.45);}
|
|
44
|
+
.fdy-chip--choice[aria-pressed="true"],.fdy-chip--filter[aria-pressed="true"]{color:var(--color-primary-strong);background:var(--color-primary-soft);border-color:rgba(var(--color-primary-rgb),.4);}
|
|
27
45
|
.fdy-chip--choice:disabled,.fdy-chip--filter:disabled,.fdy-chip--choice[aria-disabled="true"],.fdy-chip--filter[aria-disabled="true"]{opacity:.5;cursor:not-allowed;}
|
|
28
46
|
.fdy-chip__check{display:none;flex:none;width:.85rem;height:.85rem;margin-left:calc(var(--space-1)*-1);}
|
|
29
47
|
.fdy-chip__check svg{display:block;width:100%;height:100%;}
|
package/src/components/combo.css
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
.fdy-combo{position:relative;display:block;max-width:22rem;}
|
|
3
3
|
.fdy-combo__button{display:inline-flex;align-items:center;justify-content:space-between;gap:var(--space-2);width:100%;height:var(--control-h);padding:0 var(--space-3);text-align:left;cursor:pointer;font-family:var(--font-body);font-size:var(--text-sm);color:var(--color-text);background:var(--color-surface);border:1.5px solid var(--color-control-border);border-radius:var(--radius-md);box-shadow:inset 0 1px 2px rgba(16,14,30,.06);transition:border-color var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
|
|
4
4
|
.fdy-combo__button:hover{border-color:var(--color-text-muted);}
|
|
5
|
-
.fdy-combo__button:focus-visible{outline:none;border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px
|
|
6
|
-
.fdy-combo__button.is-open{border-color:var(--color-primary);box-shadow:0 0 0 3px
|
|
5
|
+
.fdy-combo__button:focus-visible{outline:none;border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px rgba(var(--color-primary-rgb),.26);}
|
|
6
|
+
.fdy-combo__button.is-open{border-color:var(--color-primary);box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
|
|
7
7
|
.fdy-combo__button:disabled,.fdy-combo__button[aria-disabled="true"]{opacity:.5;cursor:not-allowed;}
|
|
8
8
|
/* Read-only: stays focusable & shows its value at full contrast but can't open, muted surface,
|
|
9
9
|
softened border, default cursor, no focus-ring escalation (matches input[readonly], #39). */
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
.fdy-combo__button[aria-readonly="true"]:hover{border-color:var(--color-border);}
|
|
12
12
|
.fdy-combo__button[aria-readonly="true"]:focus-visible{border-color:var(--color-border-strong);box-shadow:none;}
|
|
13
13
|
.fdy-combo--error .fdy-combo__button,.fdy-combo__button[aria-invalid="true"]{border-color:var(--color-danger);}
|
|
14
|
-
.fdy-combo--error .fdy-combo__button:focus-visible,.fdy-combo--error .fdy-combo__button.is-open,.fdy-combo__button[aria-invalid="true"]:focus-visible{border-color:var(--color-danger);box-shadow:0 0 0 3px
|
|
14
|
+
.fdy-combo--error .fdy-combo__button:focus-visible,.fdy-combo--error .fdy-combo__button.is-open,.fdy-combo__button[aria-invalid="true"]:focus-visible{border-color:var(--color-danger);box-shadow:0 0 0 3px rgba(var(--color-danger-rgb),.26);}
|
|
15
15
|
.fdy-combo__value{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
16
16
|
.fdy-combo__value--placeholder{color:var(--color-text-subtle);}
|
|
17
17
|
.fdy-combo__button::after{content:"";flex:none;width:.5rem;height:.5rem;margin-top:-.15rem;border-right:2px solid var(--color-text-subtle);border-bottom:2px solid var(--color-text-subtle);transform:rotate(45deg);transition:transform var(--dur-fast) var(--ease-standard);}
|
|
@@ -26,14 +26,14 @@
|
|
|
26
26
|
.fdy-combo__option{display:flex;align-items:center;gap:var(--space-2);padding:var(--space-2) var(--space-3);border-radius:var(--radius-sm);font-size:var(--text-sm);color:var(--color-text);cursor:pointer;white-space:nowrap;}
|
|
27
27
|
.fdy-combo__option.is-highlighted{background:var(--color-surface-3);}
|
|
28
28
|
.fdy-combo__option[aria-selected="true"]{background:var(--color-primary-soft);color:var(--color-primary-strong);font-weight:var(--weight-semibold);}
|
|
29
|
-
.fdy-combo__option.is-highlighted[aria-selected="true"]{box-shadow:inset 0 0 0 2px
|
|
29
|
+
.fdy-combo__option.is-highlighted[aria-selected="true"]{box-shadow:inset 0 0 0 2px rgba(var(--color-primary-rgb),.55);}
|
|
30
30
|
/* The tick is CSS, not content (#004 §2). Inside role="option" it joined the option's ACCESSIBLE
|
|
31
31
|
NAME, so the selected option announced "✓August" while every other announced "August": the state
|
|
32
32
|
was read twice, once as aria-selected, once as decoration, and a test asking for an option by
|
|
33
33
|
its own label stopped matching the one that was selected. The `/ ""` is alt text: the glyph is
|
|
34
34
|
painted, the accessibility tree gets nothing. Same reasoning as .fdy-label--required (1.29.0).
|
|
35
35
|
The empty box still reserves its width, so labels stay aligned whether selected or not. */
|
|
36
|
-
.fdy-combo__check{display:inline-flex;justify-content:center;width:var(--space-4);flex:none;font-weight:var(--weight-bold);color:var(--color-primary);}
|
|
36
|
+
.fdy-combo__check{display:inline-flex;justify-content:center;width:var(--space-4);flex:none;font-weight:var(--weight-bold);color:var(--color-primary-strong);}
|
|
37
37
|
.fdy-combo__option[aria-selected="true"] .fdy-combo__check::after{content:"✓" / "";}
|
|
38
38
|
@media (prefers-reduced-motion:no-preference){.fdy-combo__listbox:not([hidden]){animation:fdy-combo-in var(--dur-fast) var(--ease-standard);}}
|
|
39
39
|
@keyframes fdy-combo-in{from{opacity:0;transform:translateY(-4px);}to{opacity:1;transform:none;}}
|
|
@@ -95,3 +95,39 @@
|
|
|
95
95
|
.fdy-text-success{color:var(--color-success-strong);}
|
|
96
96
|
.fdy-text-warning{color:var(--color-warning-strong);}
|
|
97
97
|
.fdy-text-danger{color:var(--color-danger-strong);}
|
|
98
|
+
/* Document columns (#065): a form or record page with an optional identity rail on the left
|
|
99
|
+
* (__lead) and a summary rail on the right (__aside). The widths follow which rails are PRESENT,
|
|
100
|
+
* so the layout is decided once here and not again by every screen:
|
|
101
|
+
* __main alone one column
|
|
102
|
+
* __main + __aside 8 / 4 (from md)
|
|
103
|
+
* __lead + __main 3 / 9 (from md)
|
|
104
|
+
* all three 3 / 6 / 3 (from lg; between md and lg both rails share the left)
|
|
105
|
+
* Stacked, the summary rail comes FIRST: last, it sits under every section, and its button with it.
|
|
106
|
+
* From md each rail is sticky and scrolls on its own, so the total and the status stay answerable
|
|
107
|
+
* while the main column scrolls with the page, and a rail taller than the screen never hangs its
|
|
108
|
+
* end off it. Screen only: a printed page is not held to a window. */
|
|
109
|
+
.fdy-page__columns{--_fdy-rail-top:var(--fdy-page-sticky-top,var(--space-4));display:grid;grid-template-columns:minmax(0,1fr);gap:var(--space-6);align-items:start;}
|
|
110
|
+
/* Under the shell the sticky topbar is in the way; --fdy-page-sticky-top overrides either default. */
|
|
111
|
+
.fdy-app .fdy-page__columns{--_fdy-rail-top:var(--fdy-page-sticky-top,calc(var(--space-16) + var(--space-4)));}
|
|
112
|
+
.fdy-page__columns > *{min-width:0;}
|
|
113
|
+
@media (max-width:959.98px){.fdy-page__aside{order:-1;}}
|
|
114
|
+
@media (min-width:960px){
|
|
115
|
+
.fdy-page__columns:has(> .fdy-page__aside){grid-template-columns:minmax(0,2fr) minmax(0,1fr);}
|
|
116
|
+
.fdy-page__columns:has(> .fdy-page__lead){grid-template-columns:minmax(0,1fr) minmax(0,3fr);}
|
|
117
|
+
/* Both rails, two columns: they stack on the left while __main spans both rows on the right. */
|
|
118
|
+
.fdy-page__columns:has(> .fdy-page__lead):has(> .fdy-page__aside) > .fdy-page__main{grid-row:span 2;}
|
|
119
|
+
}
|
|
120
|
+
@media (min-width:1280px){
|
|
121
|
+
.fdy-page__columns:has(> .fdy-page__lead):has(> .fdy-page__aside){grid-template-columns:minmax(0,1fr) minmax(0,2fr) minmax(0,1fr);}
|
|
122
|
+
.fdy-page__columns:has(> .fdy-page__lead):has(> .fdy-page__aside) > .fdy-page__main{grid-row:auto;}
|
|
123
|
+
}
|
|
124
|
+
/* The padding, taken back by the margin, is room for a focus ring at a card's edge: a scrolling box
|
|
125
|
+
clips whatever draws outside it. */
|
|
126
|
+
@media screen and (min-width:960px){
|
|
127
|
+
.fdy-page__lead,.fdy-page__aside{position:sticky;top:var(--_fdy-rail-top);max-height:calc(100dvh - var(--_fdy-rail-top) - var(--space-4));overflow-y:auto;overscroll-behavior:contain;padding:var(--space-1);margin:calc(var(--space-1) * -1);}
|
|
128
|
+
}
|
|
129
|
+
/* Two sticky rails in ONE column would slide over each other: between md and lg, with both rails
|
|
130
|
+
present, they scroll with the page instead. */
|
|
131
|
+
@media screen and (min-width:960px) and (max-width:1279.98px){
|
|
132
|
+
.fdy-page__columns:has(> .fdy-page__lead):has(> .fdy-page__aside) > :is(.fdy-page__lead,.fdy-page__aside){position:static;max-height:none;overflow:visible;}
|
|
133
|
+
}
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
.fdy-field>.fdy-datepicker,.fdy-field>.fdy-timepicker,.fdy-field>.fdy-daterange{max-width:none;}
|
|
10
10
|
.fdy-datepicker__trigger{display:inline-flex;align-items:center;gap:var(--space-2);width:100%;height:var(--control-h);padding:0 var(--space-3);text-align:left;cursor:pointer;font-family:var(--font-body);font-size:var(--text-sm);color:var(--color-text);background:var(--color-surface);border:1.5px solid var(--color-control-border);border-radius:var(--radius-md);box-shadow:inset 0 1px 2px rgba(16,14,30,.06);transition:border-color var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
|
|
11
11
|
.fdy-datepicker__trigger:hover{border-color:var(--color-text-muted);}
|
|
12
|
-
.fdy-datepicker__trigger:focus-visible{outline:none;border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px
|
|
13
|
-
.fdy-datepicker__trigger.is-open{border-color:var(--color-primary);box-shadow:0 0 0 3px
|
|
12
|
+
.fdy-datepicker__trigger:focus-visible{outline:none;border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px rgba(var(--color-primary-rgb),.26);}
|
|
13
|
+
.fdy-datepicker__trigger.is-open{border-color:var(--color-primary);box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
|
|
14
14
|
.fdy-datepicker--error .fdy-datepicker__trigger,.fdy-datepicker__trigger[aria-invalid="true"]{border-color:var(--color-danger);}
|
|
15
|
-
.fdy-datepicker--error .fdy-datepicker__trigger:focus-visible,.fdy-datepicker--error .fdy-datepicker__trigger.is-open,.fdy-datepicker__trigger[aria-invalid="true"]:focus-visible{border-color:var(--color-danger);box-shadow:0 0 0 3px
|
|
15
|
+
.fdy-datepicker--error .fdy-datepicker__trigger:focus-visible,.fdy-datepicker--error .fdy-datepicker__trigger.is-open,.fdy-datepicker__trigger[aria-invalid="true"]:focus-visible{border-color:var(--color-danger);box-shadow:0 0 0 3px rgba(var(--color-danger-rgb),.26);}
|
|
16
16
|
.fdy-datepicker__trigger:disabled,.fdy-datepicker__trigger[aria-disabled="true"]{opacity:.5;cursor:not-allowed;}
|
|
17
17
|
.fdy-datepicker__trigger[aria-readonly="true"]{background:var(--color-surface-2);border-color:var(--color-border);cursor:default;}
|
|
18
18
|
.fdy-datepicker__trigger[aria-readonly="true"]:hover{border-color:var(--color-border);}
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
.fdy-datepicker__icon.is-hidden{visibility:hidden;}
|
|
28
28
|
.fdy-datepicker__clear{position:absolute;top:50%;right:var(--space-3);transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;width:1.15rem;height:1.15rem;padding:0;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-text-subtle);cursor:pointer;transition:color var(--dur-fast) var(--ease-standard),background var(--dur-fast) var(--ease-standard);}
|
|
29
29
|
.fdy-datepicker__clear:hover{color:var(--color-text);background:var(--color-surface-2);}
|
|
30
|
-
.fdy-datepicker__clear:focus-visible{outline:none;color:var(--color-text);box-shadow:0 0 0 3px
|
|
30
|
+
.fdy-datepicker__clear:focus-visible{outline:none;color:var(--color-text);box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
|
|
31
31
|
.fdy-datepicker__clear svg{display:block;width:.9rem;height:.9rem;}
|
|
32
32
|
|
|
33
33
|
.fdy-datepicker__panel{position:absolute;top:calc(100% + var(--space-1));left:0;z-index:130;padding:var(--space-3);background:var(--color-surface);color:var(--color-text);border:var(--bw) solid var(--color-border-strong);border-radius:var(--radius-md);box-shadow:var(--shadow-3);}
|
|
@@ -39,16 +39,16 @@
|
|
|
39
39
|
reset and the same hover/focus treatment as the arrows beside it. */
|
|
40
40
|
.fdy-cal__title{flex:1;appearance:none;border:0;background:transparent;margin:0;padding:var(--space-1) var(--space-2);border-radius:var(--radius-sm);text-align:center;font-family:inherit;font-weight:var(--weight-semibold);font-size:var(--text-sm);color:var(--color-text);text-transform:capitalize;cursor:pointer;}
|
|
41
41
|
.fdy-cal__title:hover{background:var(--color-surface-2);}
|
|
42
|
-
.fdy-cal__title:focus-visible{outline:none;box-shadow:0 0 0 3px
|
|
42
|
+
.fdy-cal__title:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
|
|
43
43
|
.fdy-cal__month,.fdy-cal__year{display:inline-flex;align-items:center;justify-content:center;height:2.25rem;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-text);font-family:inherit;font-size:var(--text-sm);text-transform:capitalize;cursor:pointer;}
|
|
44
44
|
.fdy-cal__month:hover,.fdy-cal__year:hover{background:var(--color-surface-2);}
|
|
45
|
-
.fdy-cal__month:focus-visible,.fdy-cal__year:focus-visible{outline:none;box-shadow:0 0 0 3px
|
|
45
|
+
.fdy-cal__month:focus-visible,.fdy-cal__year:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
|
|
46
46
|
.fdy-cal__month:disabled,.fdy-cal__year:disabled{opacity:.4;cursor:not-allowed;background:transparent;}
|
|
47
47
|
.fdy-cal__month.is-today,.fdy-cal__year.is-today{font-weight:var(--weight-bold);box-shadow:inset 0 0 0 1.5px var(--color-border-strong);}
|
|
48
48
|
.fdy-cal__month.is-selected,.fdy-cal__year.is-selected{background:var(--color-primary);color:var(--color-on-primary);font-weight:var(--weight-semibold);}
|
|
49
49
|
.fdy-cal__nav{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;flex:none;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-text-muted);font-size:var(--text-lg);line-height:1;cursor:pointer;}
|
|
50
50
|
.fdy-cal__nav:hover{background:var(--color-surface-2);color:var(--color-text);}
|
|
51
|
-
.fdy-cal__nav:focus-visible{outline:none;box-shadow:0 0 0 3px
|
|
51
|
+
.fdy-cal__nav:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
|
|
52
52
|
.fdy-cal__grid{display:grid;grid-template-columns:repeat(7,2rem);gap:2px;}
|
|
53
53
|
/* Month grid. 3×4 over the same track width as the 7-column day grid, so the panel does not resize
|
|
54
54
|
when you drill and the pointer does not have to travel.
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
.fdy-cal__dow{display:flex;align-items:center;justify-content:center;height:1.75rem;font-size:var(--text-xs);font-weight:var(--weight-semibold);color:var(--color-text-muted);text-transform:capitalize;}
|
|
61
61
|
.fdy-cal__day{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-text);font-size:var(--text-sm);font-variant-numeric:tabular-nums;cursor:pointer;}
|
|
62
62
|
.fdy-cal__day:hover{background:var(--color-surface-2);}
|
|
63
|
-
.fdy-cal__day:focus-visible{outline:none;box-shadow:0 0 0 3px
|
|
63
|
+
.fdy-cal__day:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
|
|
64
64
|
.fdy-cal__day.is-outside{color:var(--color-text-subtle);opacity:.55;}
|
|
65
65
|
.fdy-cal__day.is-today{font-weight:var(--weight-bold);box-shadow:inset 0 0 0 1.5px var(--color-border-strong);}
|
|
66
66
|
.fdy-cal__day.in-range{background:var(--color-primary-soft);color:var(--color-primary-strong);border-radius:0;}
|