@cahyo-dimas/freeday 1.53.0 → 2.0.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 +657 -535
- package/COMPONENTS.md +153 -135
- package/README.id.md +46 -45
- package/README.md +46 -43
- package/USAGE.md +39 -35
- package/adapters/blazor/FdyAppShell.razor +50 -0
- package/adapters/blazor/FdyAppShell.razor.cs +114 -0
- package/adapters/blazor/FdyCfl.razor.cs +1 -1
- package/adapters/blazor/FdyChart.razor +1 -1
- package/adapters/blazor/FdyChart.razor.cs +3 -3
- package/adapters/blazor/FdyChartSeries.cs +1 -1
- package/adapters/blazor/FdyCombo.razor.cs +2 -2
- package/adapters/blazor/FdyModal.razor +1 -1
- package/adapters/blazor/FdyTable.razor.cs +7 -7
- package/adapters/blazor/FdyTableFilter.razor +1 -1
- package/adapters/blazor/FdyTableFooter.razor +1 -1
- package/adapters/blazor/FdyTableFooter.razor.cs +4 -4
- package/adapters/blazor/Freeday.Blazor.csproj +1 -1
- package/adapters/blazor/FreedayComponentBase.cs +1 -1
- package/adapters/blazor/TableModel.cs +3 -3
- package/adapters/blazor/TableTypes.cs +3 -3
- package/adapters/blazor/freeday-blazor.js +5 -5
- package/adapters/core/app-shell.d.ts +30 -0
- package/adapters/core/app-shell.js +134 -0
- package/adapters/core/table-model.d.ts +2 -2
- package/adapters/core/table-model.js +8 -8
- package/adapters/react/components/FdyAppShell.tsx +159 -0
- package/adapters/react/components/FdyAutocomplete.tsx +1 -1
- package/adapters/react/components/FdyCascade.tsx +1 -1
- package/adapters/react/components/FdyCfl.tsx +11 -11
- package/adapters/react/components/FdyDateRange.tsx +2 -2
- package/adapters/react/components/FdyDatepicker.tsx +5 -5
- package/adapters/react/components/FdyDrawer.tsx +1 -1
- package/adapters/react/components/FdyModal.tsx +2 -2
- package/adapters/react/components/FdyTable.tsx +7 -7
- package/adapters/react/components/FdyTableFilter.tsx +1 -1
- package/adapters/react/components/FdyTableFooter.tsx +5 -5
- package/adapters/react/index.d.ts +2 -1
- package/adapters/react/index.js +1 -0
- package/adapters/react/useFreeday.js +3 -3
- package/adapters/vue/components/FdyAppShell.vue +160 -0
- package/adapters/vue/components/FdyAutocomplete.vue +1 -1
- package/adapters/vue/components/FdyCascade.vue +1 -1
- package/adapters/vue/components/FdyCfl.vue +8 -8
- package/adapters/vue/components/FdyChart.vue +2 -2
- package/adapters/vue/components/FdyCombo.vue +1 -1
- package/adapters/vue/components/FdyDateRange.vue +2 -2
- package/adapters/vue/components/FdyDatepicker.vue +5 -5
- package/adapters/vue/components/FdyDrawer.vue +3 -3
- package/adapters/vue/components/FdyModal.vue +3 -3
- package/adapters/vue/components/FdyTable.vue +7 -7
- package/adapters/vue/components/FdyTableFilter.vue +1 -1
- package/adapters/vue/components/FdyTableFooter.vue +5 -5
- package/adapters/vue/index.d.ts +3 -2
- package/adapters/vue/index.js +1 -0
- package/adapters/vue/useFreeday.js +3 -3
- package/dist/asset.d.ts +3 -3
- package/dist/freeday-app-shell.js +49 -9
- package/dist/freeday-autocomplete.js +2 -2
- package/dist/freeday-breakpoint.js +1 -1
- package/dist/freeday-carousel.js +5 -5
- package/dist/freeday-cascade.js +7 -7
- package/dist/freeday-cfl.js +9 -9
- package/dist/freeday-chart.js +11 -11
- package/dist/freeday-chip.js +3 -3
- package/dist/freeday-datepicker.js +11 -7
- package/dist/freeday-datetime.js +5 -5
- package/dist/freeday-drawer.js +1 -1
- package/dist/freeday-form.js +14 -14
- package/dist/freeday-mask.js +6 -6
- package/dist/freeday-menu.js +1 -1
- package/dist/freeday-number.js +3 -3
- package/dist/freeday-popover.js +3 -3
- package/dist/freeday-rating.js +2 -2
- package/dist/freeday-select.js +5 -5
- package/dist/freeday-slider.js +2 -2
- package/dist/freeday-stepper.js +7 -7
- package/dist/freeday-table.js +15 -15
- package/dist/freeday-tabs.js +1 -1
- package/dist/freeday-timepicker.js +2 -2
- package/dist/freeday-toast.js +8 -8
- package/dist/freeday-tree.js +3 -3
- package/dist/freeday-upload.js +25 -25
- package/dist/freeday.bundle.css +176 -176
- package/dist/freeday.css +170 -170
- package/dist/freeday.d.ts +2 -2
- package/dist/freeday.js +202 -158
- package/dist/freeday.tokens.css +5 -5
- package/docs/agent-onboarding.md +42 -42
- package/docs/getting-started.md +35 -35
- package/docs/integrations.md +18 -18
- package/docs/reference-screen.html +14 -14
- package/package.json +5 -4
- package/src/base.css +6 -6
- package/src/components/accordion.css +4 -4
- package/src/components/alert.css +1 -1
- package/src/components/app-shell.css +10 -10
- package/src/components/appbar.css +3 -3
- package/src/components/autocomplete.css +1 -1
- package/src/components/avatar.css +3 -3
- package/src/components/badge.css +5 -5
- package/src/components/breadcrumb.css +1 -1
- package/src/components/breakpoints.css +4 -4
- package/src/components/button.css +13 -13
- package/src/components/card.css +8 -8
- package/src/components/carousel.css +2 -2
- package/src/components/cascade.css +2 -2
- package/src/components/cfl.css +4 -4
- package/src/components/chart.css +3 -3
- package/src/components/chip.css +2 -2
- package/src/components/combo.css +3 -3
- package/src/components/composition.css +7 -7
- package/src/components/datepicker.css +6 -6
- package/src/components/datetimepicker.css +1 -1
- package/src/components/description-list.css +1 -1
- package/src/components/divider.css +1 -1
- package/src/components/drawer.css +1 -1
- package/src/components/file-upload.css +5 -5
- package/src/components/filterbar.css +7 -7
- package/src/components/form-grid.css +2 -2
- package/src/components/input-group.css +6 -6
- package/src/components/input.css +6 -6
- package/src/components/kbd.css +1 -1
- package/src/components/list.css +6 -6
- package/src/components/menu.css +3 -3
- package/src/components/modal.css +1 -1
- package/src/components/pagination.css +1 -1
- package/src/components/progress.css +1 -1
- package/src/components/rating.css +2 -2
- package/src/components/selection.css +4 -4
- package/src/components/skeleton.css +1 -1
- package/src/components/slider.css +1 -1
- package/src/components/spinner.css +1 -1
- package/src/components/states.css +1 -1
- package/src/components/stepper.css +2 -2
- package/src/components/table.css +15 -15
- package/src/components/tabs.css +3 -3
- package/src/components/timeline.css +1 -1
- package/src/components/timepicker.css +1 -1
- package/src/components/toast.css +3 -3
- package/src/components/tooltip.css +1 -1
- package/src/components/tree.css +2 -2
- package/tokens/breakpoints.d.ts +2 -2
- package/tokens/breakpoints.mjs +3 -3
- package/tokens/tokens.json +1 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<title>Freeday — reference screen</title>
|
|
7
7
|
|
|
8
8
|
<!-- The kit in one file: tokens + components (this is what @cahyo-dimas/freeday/css resolves to).
|
|
9
|
-
Note the file name: dist/freeday.css is components ONLY
|
|
9
|
+
Note the file name: dist/freeday.css is components ONLY. Link that alone and every
|
|
10
10
|
var(--color-*) / var(--space-*) silently falls back, which looks like a broken stylesheet.
|
|
11
11
|
Paths are relative, so this works both in the repo and in node_modules/@cahyo-dimas/freeday/docs/. -->
|
|
12
12
|
<link rel="stylesheet" href="../dist/freeday.bundle.css">
|
|
@@ -18,17 +18,17 @@
|
|
|
18
18
|
<body>
|
|
19
19
|
|
|
20
20
|
<!-- ══════════════════════════════════════════════════════════════════════════════════════════
|
|
21
|
-
REFERENCE SCREEN
|
|
21
|
+
REFERENCE SCREEN: one business screen, assembled the intended way.
|
|
22
22
|
|
|
23
23
|
Structure, outermost to innermost:
|
|
24
24
|
.fdy-app the shell (topbar · sidebar · main)
|
|
25
|
-
└ .fdy-page the screen body
|
|
25
|
+
└ .fdy-page the screen body, vertical section rhythm
|
|
26
26
|
├ .fdy-page__header eyebrow + page title + desc | ONE primary action
|
|
27
|
-
├ .fdy-page-section KPI strip (.fdy-stats
|
|
27
|
+
├ .fdy-page-section KPI strip (.fdy-stats, deliberately not cards)
|
|
28
28
|
├ .fdy-page-section chart
|
|
29
29
|
└ .fdy-page-section the data table
|
|
30
30
|
|
|
31
|
-
Every class here exists in COMPONENTS.md. Every value comes from a token
|
|
31
|
+
Every class here exists in COMPONENTS.md. Every value comes from a token. Search this file
|
|
32
32
|
for "#" or "px" and you will find none outside the demo-only sizing on the chart wrappers.
|
|
33
33
|
Doctrine behind the choices: USAGE.md.
|
|
34
34
|
══════════════════════════════════════════════════════════════════════════════════════════ -->
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
</aside>
|
|
72
72
|
|
|
73
73
|
<!-- __content is the column wrapper holding the topbar + main. It exists so the sticky topbar
|
|
74
|
-
has a tall containing block to travel in
|
|
74
|
+
has a tall containing block to travel in, which a bare flex cell cannot give it. -->
|
|
75
75
|
<div class="fdy-app__content">
|
|
76
76
|
|
|
77
77
|
<header class="fdy-app__topbar">
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
</div>
|
|
92
92
|
|
|
93
93
|
<span class="fdy-badge-wrap">
|
|
94
|
-
<button class="fdy-btn fdy-btn--ghost fdy-btn--icon" type="button" aria-label="Notifications
|
|
94
|
+
<button class="fdy-btn fdy-btn--ghost fdy-btn--icon" type="button" aria-label="Notifications, 3 new">
|
|
95
95
|
<span class="fdy-btn__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.7 21a2 2 0 0 1-3.4 0"/></svg></span>
|
|
96
96
|
</button>
|
|
97
97
|
<span class="fdy-badge-ov fdy-badge-ov--danger" aria-hidden="true">3</span>
|
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
<!-- ═══ THE SCREEN ═══ -->
|
|
118
118
|
<div class="fdy-page">
|
|
119
119
|
|
|
120
|
-
<!-- One page header. One primary action
|
|
120
|
+
<!-- One page header. One primary action; everything else is ghost. -->
|
|
121
121
|
<header class="fdy-page__header">
|
|
122
122
|
<div class="fdy-page__heading">
|
|
123
123
|
<p class="fdy-eyebrow">Sales</p>
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
</div>
|
|
131
131
|
</header>
|
|
132
132
|
|
|
133
|
-
<!-- KPI strip: .fdy-stat tiles, NOT cards
|
|
133
|
+
<!-- KPI strip: .fdy-stat tiles, NOT cards, because a metric row must not read as a card grid. -->
|
|
134
134
|
<section class="fdy-page-section" aria-labelledby="kpi-h">
|
|
135
135
|
<h3 class="fdy-title-section fdy-visually-hidden" id="kpi-h">Key figures</h3>
|
|
136
136
|
<div class="fdy-stats fdy-stats--boxed">
|
|
@@ -301,7 +301,7 @@
|
|
|
301
301
|
</div>
|
|
302
302
|
|
|
303
303
|
<!-- Below md: the SAME rows as a flat list. .fdy-list is the flat container the doctrine
|
|
304
|
-
asks for
|
|
304
|
+
asks for. A stack of .fdy-card would put a 34px lift under every row.
|
|
305
305
|
In a framework app you would not duplicate the markup: hide <FdyTable> below md, feed
|
|
306
306
|
this list from its `process` event, and drive both from a controlled `pageIndex` with
|
|
307
307
|
ONE pager rendered outside the table. See COMPONENTS.md §Data table. -->
|
|
@@ -339,7 +339,7 @@
|
|
|
339
339
|
<!-- A detail region: key-value pairs + a timeline. Flat section, one card each. -->
|
|
340
340
|
<section class="fdy-page-section" aria-labelledby="detail-h">
|
|
341
341
|
<div class="fdy-page-section__head">
|
|
342
|
-
<h3 class="fdy-title-section" id="detail-h">INV-2042
|
|
342
|
+
<h3 class="fdy-title-section" id="detail-h">INV-2042 detail</h3>
|
|
343
343
|
<button class="fdy-btn fdy-btn--text" type="button">Open full record</button>
|
|
344
344
|
</div>
|
|
345
345
|
<div class="fdy-card">
|
|
@@ -419,10 +419,10 @@
|
|
|
419
419
|
|
|
420
420
|
<script>
|
|
421
421
|
// The nav toggle is the kit's now: `data-fdy-app` on the shell is the whole wiring. What used to
|
|
422
|
-
// live here handled the two state classes and nothing else
|
|
423
|
-
// background, no focus restore
|
|
422
|
+
// live here handled the two state classes and nothing else: no Escape, no focus trap, no inert
|
|
423
|
+
// background, no focus restore. That is why the behaviour moved into the kit.
|
|
424
424
|
|
|
425
|
-
// Theme + density live on <html>, set once
|
|
425
|
+
// Theme + density live on <html>, set once, never per component.
|
|
426
426
|
const root = document.documentElement;
|
|
427
427
|
document.getElementById('toggle-theme').addEventListener('click', () => {
|
|
428
428
|
root.dataset.theme = root.dataset.theme === 'dark' ? 'light' : 'dark';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cahyo-dimas/freeday",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Freeday
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Freeday: token-driven, framework-agnostic UI KIT (design source-of-truth).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"sideEffects": true,
|
|
@@ -92,10 +92,11 @@
|
|
|
92
92
|
"scripts": {
|
|
93
93
|
"build": "node tokens/build.mjs",
|
|
94
94
|
"test": "node --test",
|
|
95
|
-
"test:browser": "node --test 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/cfl-multi.mjs browser/crowding.mjs browser/over-dialog.mjs browser/chart-scale.mjs browser/overlay-stack.mjs browser/chart-a11y.mjs browser/app-shell.mjs",
|
|
95
|
+
"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/cfl-multi.mjs browser/crowding.mjs browser/over-dialog.mjs browser/chart-scale.mjs browser/overlay-stack.mjs browser/chart-a11y.mjs browser/app-shell.mjs",
|
|
96
96
|
"prepack": "node tokens/build.mjs",
|
|
97
97
|
"version": "node tokens/build.mjs && git add dist",
|
|
98
|
-
"typecheck:react": "tsc -p adapters/react/tsconfig.json --noEmit"
|
|
98
|
+
"typecheck:react": "tsc -p adapters/react/tsconfig.json --noEmit",
|
|
99
|
+
"test:blazor": "dotnet test test/blazor -v quiet --nologo"
|
|
99
100
|
},
|
|
100
101
|
"devDependencies": {
|
|
101
102
|
"@types/react": "^19.2.17",
|
package/src/base.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday base layer
|
|
1
|
+
/* Freeday base layer: reset ringan + default via token */
|
|
2
2
|
*, *::before, *::after { box-sizing: border-box; }
|
|
3
3
|
html { -webkit-text-size-adjust: 100%; }
|
|
4
4
|
body {
|
|
@@ -16,21 +16,21 @@ a { color: var(--color-primary); }
|
|
|
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
|
|
18
18
|
* has a position, and because it is position:absolute its containing block is the nearest
|
|
19
|
-
* POSITIONED ancestor
|
|
19
|
+
* POSITIONED ancestor, and with none, the initial containing block (the document itself).
|
|
20
20
|
*
|
|
21
21
|
* That matters, because `overflow` only clips a descendant whose containing block is inside the
|
|
22
22
|
* overflow box. So a hidden span in a horizontally scrolling table (the kit's own recommended way
|
|
23
|
-
* to name an icon button) parks at its static position
|
|
23
|
+
* to name an icon button) parks at its static position, possibly thousands of px to the right —
|
|
24
24
|
* and drags the whole DOCUMENT sideways: invisible, and immune to `overflow-x:hidden` on every
|
|
25
25
|
* wrapper. Measured before the fix: 1351px of phantom page scroll from 11 spans.
|
|
26
26
|
*
|
|
27
|
-
* The fix is not here
|
|
27
|
+
* The fix is not here but on the containers: every clipping/scrolling container in the kit
|
|
28
28
|
* that holds consumer markup declares `position:relative` so it becomes the containing block for
|
|
29
29
|
* its own out-of-flow content (see test/css.test.mjs, which will not let a new one skip it).
|
|
30
|
-
* Note the measurement trap: documentElement.scrollWidth sees it, body.scrollWidth does not
|
|
30
|
+
* Note the measurement trap: documentElement.scrollWidth sees it, body.scrollWidth does not,
|
|
31
31
|
* the honest check is `window.scrollTo(9999, 0)` then reading `window.scrollX`. */
|
|
32
32
|
.fdy-visually-hidden { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
|
|
33
|
-
/* Opt-in list reset. base.css is intentionally a *light* reset
|
|
33
|
+
/* Opt-in list reset. base.css is intentionally a *light* reset: it does NOT strip list/paragraph
|
|
34
34
|
* margins (see the kit's own list components, which each reset themselves). If you run a utility
|
|
35
35
|
* framework with its preflight OFF (the recommended pairing), a semantic <ul>/<ol> keeps native
|
|
36
36
|
* bullets + a 40px indent; add this class (or a Freeday list component) to normalise it. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/* Freeday
|
|
2
|
-
/* position:relative
|
|
1
|
+
/* Freeday. Accordion (native <details>/<summary>, zero-JS) */
|
|
2
|
+
/* position:relative, containing block for out-of-flow panel content (see base.css). NOT redundant
|
|
3
3
|
with the panel's reveal animation: that animation gives the panel a transform (which happens to
|
|
4
|
-
make it a containing block too), but it lives behind prefers-reduced-motion:no-preference
|
|
4
|
+
make it a containing block too), but it lives behind prefers-reduced-motion:no-preference, so
|
|
5
5
|
without this line the escape bug appears ONLY for readers who asked for reduced motion. */
|
|
6
6
|
.fdy-accordion{position:relative;border:var(--bw) solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;background:var(--color-surface);}
|
|
7
7
|
.fdy-accordion__item + .fdy-accordion__item{border-top:var(--bw) solid var(--color-border);}
|
|
@@ -13,5 +13,5 @@
|
|
|
13
13
|
.fdy-accordion__item > summary:focus-visible{outline:none;box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
|
|
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
|
-
* browser without JS, so we animate the revealed panel instead
|
|
16
|
+
* browser without JS, so we animate the revealed panel instead, cheap, safe, reduced-motion gated. */
|
|
17
17
|
@media (prefers-reduced-motion:no-preference){.fdy-accordion__item[open] > .fdy-accordion__panel{animation:fdy-combo-in var(--dur-base) var(--ease-standard);}}
|
package/src/components/alert.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Alert (inline) */
|
|
2
2
|
.fdy-alert{display:flex;gap:var(--space-3);padding:var(--space-3) var(--space-4);border-radius:var(--radius-md);border:var(--bw) solid var(--color-border);font-size:var(--text-sm);color:var(--color-text);background:var(--color-surface-2);}
|
|
3
3
|
.fdy-alert__icon{flex:none;display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;margin-top:.05rem;}
|
|
4
4
|
.fdy-alert__icon svg{width:100%;height:100%;}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. App Shell.
|
|
2
2
|
* Natural document scroll: the shell grows with content and the PAGE scrolls,
|
|
3
3
|
* while the sidebar and topbar are position:sticky so they stay in view. No
|
|
4
4
|
* fixed-height / internal-scroll trickery (that leaves a white gap if the host
|
|
5
5
|
* page can still scroll). Structure: .fdy-app > (.fdy-app__sidebar, .fdy-app__content
|
|
6
6
|
* > (.fdy-app__topbar, .fdy-app__main)). The __content wrapper gives the sticky
|
|
7
|
-
* topbar a tall containing block so it can travel
|
|
7
|
+
* topbar a tall containing block so it can travel, a bare grid cell can't. */
|
|
8
8
|
.fdy-app{display:flex;align-items:stretch;min-height:100vh;background:var(--color-surface-2);}
|
|
9
9
|
.fdy-app__sidebar{position:sticky;top:0;align-self:flex-start;height:100vh;overflow:hidden;flex:none;width:15.5rem;box-sizing:border-box;background:var(--color-surface);border-right:var(--bw) solid var(--color-border);display:flex;flex-direction:column;transition:width var(--dur-slow) var(--ease-standard),border-right-width var(--dur-slow) var(--ease-standard);}
|
|
10
10
|
.fdy-app__content{flex:1;min-width:0;display:flex;flex-direction:column;}
|
|
11
|
-
/* Brand is a fixed header
|
|
11
|
+
/* Brand is a fixed header, same height as the topbar so they align, and only
|
|
12
12
|
* the nav below it scrolls. Bare text (no children) renders as one bold
|
|
13
13
|
* display line via the rules on .fdy-app__brand itself. For a mark + two-line
|
|
14
14
|
* title/subtitle, add structured children mirroring .fdy-appbar__brand:
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
.fdy-app__brand-title{display:block;font-family:var(--font-display);font-weight:var(--weight-bold);font-size:var(--text-lg);color:var(--color-text);margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
25
25
|
.fdy-app__brand-subtitle{display:block;font-size:var(--text-xs);font-weight:var(--weight-regular);color:var(--color-text-muted);margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
26
26
|
.fdy-nav{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;display:flex;flex-direction:column;gap:2px;padding:var(--space-3) var(--space-3) var(--space-6);}
|
|
27
|
-
/* Horizontal variant
|
|
27
|
+
/* Horizontal variant, the SAME nav links laid out as a row, for a top-nav application (one that
|
|
28
28
|
* puts its primary navigation in `.fdy-appbar` / `.fdy-app__topbar` and has no sidebar). Deliberately
|
|
29
29
|
* a modifier and not a new block: the item, its states and `aria-current="page"` are unchanged. That
|
|
30
|
-
* is the point
|
|
30
|
+
* is the point, a navigation link is a link marked `aria-current`, never `aria-selected` (invalid
|
|
31
31
|
* ARIA on an anchor) and never a tab role, so borrowing `.fdy-tabs` for routes half-adopts a contract
|
|
32
32
|
* (roving tabindex, a panel per tab) that route links do not honour. position:relative because the
|
|
33
33
|
* row scrolls; see base.css on why a scroller must be a containing block. */
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
.fdy-nav__item[aria-current="page"]{background:var(--color-primary-soft);color:var(--color-primary-strong);font-weight:var(--weight-semibold);}
|
|
40
40
|
.fdy-nav__item:focus-visible{outline:none;box-shadow:0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
|
|
41
41
|
/* Sidebar-menu extras: per-node icon (leading), count badge (trailing), and a
|
|
42
|
-
* text label that truncates. Any of these are optional
|
|
42
|
+
* text label that truncates. Any of these are optional, a bare text item works. */
|
|
43
43
|
.fdy-nav__icon{flex:none;display:inline-flex;width:1.15rem;height:1.15rem;}
|
|
44
44
|
.fdy-nav__icon svg{display:block;width:100%;height:100%;}
|
|
45
45
|
.fdy-nav__label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
.fdy-nav__group{margin:0;}
|
|
50
50
|
.fdy-nav__group + .fdy-nav__group,.fdy-nav__item + .fdy-nav__group{border-top:var(--bw) solid var(--color-border-muted);margin-top:var(--space-1);padding-top:var(--space-1);}
|
|
51
51
|
/* Prose, and a control name: this is the <summary> of a nav group, at --text-xs uppercase. It is
|
|
52
|
-
the second class found spending the decorative ink on type (after .fdy-eyebrow)
|
|
52
|
+
the second class found spending the decorative ink on type (after .fdy-eyebrow), and it was
|
|
53
53
|
already brightening to --color-text-muted on hover, which is the rule admitting its resting
|
|
54
54
|
state was too faint to read. */
|
|
55
55
|
.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);}
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
.fdy-nav__grouplabel:focus-visible{outline:none;border-radius:var(--radius-sm);box-shadow:0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
|
|
59
59
|
.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;}
|
|
60
60
|
.fdy-nav__group[open] > .fdy-nav__grouplabel::after{transform:rotate(45deg);}
|
|
61
|
-
/* --flat: calmer, non-collapsible grouped nav
|
|
61
|
+
/* --flat: calmer, non-collapsible grouped nav, hide the caret, drop the
|
|
62
62
|
* inter-group divider and the collapse cursor. Works on native <details> groups
|
|
63
63
|
* (caret just hidden) and on static <div>/<p> groups (never collapsible). */
|
|
64
64
|
.fdy-nav--flat .fdy-nav__grouplabel::after{display:none;}
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
.fdy-nav__tree > summary .fdy-nav__caret{flex:none;margin-left:auto;width:.4rem;height:.4rem;border-right:2px solid currentColor;border-bottom:2px solid currentColor;transform:rotate(-45deg);transition:transform var(--dur-fast) var(--ease-standard);opacity:.6;}
|
|
72
72
|
.fdy-nav__tree[open] > summary .fdy-nav__caret{transform:rotate(45deg);}
|
|
73
73
|
.fdy-nav__sub{display:flex;flex-direction:column;gap:2px;padding-left:calc(var(--space-3) + 1.15rem);margin-top:2px;}
|
|
74
|
-
/* Collapse toggle (hamburger)
|
|
74
|
+
/* Collapse toggle (hamburger), sits in the topbar; collapses the sidebar. */
|
|
75
75
|
.fdy-app__navtoggle{display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;flex:none;padding:0;border:0;border-radius:var(--radius-md);background:transparent;color:var(--color-text-muted);cursor:pointer;transition:background var(--dur-fast) var(--ease-standard),color var(--dur-fast) var(--ease-standard);}
|
|
76
76
|
.fdy-app__navtoggle:hover{background:var(--color-surface-2);color:var(--color-text);}
|
|
77
77
|
.fdy-app__navtoggle:focus-visible{outline:none;box-shadow:0 0 0 2px var(--color-primary);}
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
.fdy-app__main{flex:1;padding:var(--space-8);}
|
|
85
85
|
.fdy-skip{position:absolute;left:var(--space-2);top:var(--space-2);background:var(--color-primary);color:var(--color-on-primary);padding:var(--space-2) var(--space-4);border-radius:var(--radius-md);z-index:100;transform:translateY(-250%);transition:transform var(--dur-fast) var(--ease-standard);}
|
|
86
86
|
.fdy-skip:focus{transform:translateY(0);}
|
|
87
|
-
/* --static: for a bounded/embedded shell (a demo box, not the whole page)
|
|
87
|
+
/* --static: for a bounded/embedded shell (a demo box, not the whole page),
|
|
88
88
|
* drop sticky so nothing escapes the box; sidebar stretches to fill it. */
|
|
89
89
|
.fdy-app--static .fdy-app__sidebar{position:static;height:auto;align-self:stretch;}
|
|
90
90
|
.fdy-app--static .fdy-app__topbar{position:static;}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. App bar (standalone top bar: brand · actions · spacer).
|
|
2
2
|
* Distinct from .fdy-app__topbar (which is bound to the shell grid). Use anywhere;
|
|
3
3
|
* sticky / elevated / dense / coloured via modifiers. Pure CSS. */
|
|
4
4
|
.fdy-appbar{display:flex;align-items:center;gap:var(--space-3);height:var(--space-16);padding:0 var(--space-4);background:var(--color-surface);color:var(--color-text);border-bottom:var(--bw) solid var(--color-border);}
|
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
.fdy-appbar__spacer{flex:1;}
|
|
12
12
|
.fdy-appbar__actions{display:inline-flex;align-items:center;gap:var(--space-1);}
|
|
13
13
|
/* Primary navigation inside the bar: `<nav class="fdy-nav fdy-nav--horizontal">` with
|
|
14
|
-
`.fdy-nav__item` links (see app-shell.css). The bar ships no link class of its own
|
|
14
|
+
`.fdy-nav__item` links (see app-shell.css). The bar ships no link class of its own, a nav link
|
|
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
18
|
.fdy-appbar--primary .fdy-btn--ghost:hover{background:color-mix(in srgb,var(--color-on-primary) 16%,transparent);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
|
-
wash plus the semibold weight the base rule already applies
|
|
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
23
|
.fdy-appbar--primary .fdy-nav__item:hover{background:color-mix(in srgb,var(--color-on-primary) 16%,transparent);color:var(--color-on-primary);}
|
|
24
24
|
.fdy-appbar--primary .fdy-nav__item[aria-current="page"]{background:color-mix(in srgb,var(--color-on-primary) 24%,transparent);color:var(--color-on-primary);}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Autocomplete (text input + filtered suggestion listbox, APG editable combobox).
|
|
2
2
|
* Enhanced by freeday-autocomplete.js. */
|
|
3
3
|
.fdy-autocomplete{position:relative;display:block;max-width:22rem;}
|
|
4
4
|
.fdy-autocomplete__listbox{position:absolute;top:calc(100% + var(--space-1));left:0;z-index:120;min-width:100%;max-height:16rem;overflow-y:auto;margin:0;padding:var(--space-1);list-style:none;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);}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Avatar */
|
|
2
2
|
.fdy-avatar{display:inline-flex;align-items:center;justify-content:center;flex:none;width:2.5rem;height:2.5rem;border-radius:var(--radius-full);background:var(--color-primary-soft);color:var(--color-primary-strong);font-family:var(--font-body);font-weight:var(--weight-semibold);font-size:var(--text-sm);line-height:1;overflow:hidden;user-select:none;}
|
|
3
3
|
.fdy-avatar img{width:100%;height:100%;object-fit:cover;}
|
|
4
4
|
/* --xs exists for an avatar INSIDE a control: `.fdy-btn--sm` is exactly 2rem tall, which is also
|
|
5
5
|
`--sm`'s size, so a small avatar in a small button fills it edge to edge and the ghost border
|
|
6
6
|
crosses the circle. 1.5rem leaves the same ~4px breathing room the kit's controls give their
|
|
7
|
-
icons. Uses `--text-xs` (the smallest type token the kit has
|
|
7
|
+
icons. Uses `--text-xs` (the smallest type token the kit has, there is no `2xs`). */
|
|
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
11
|
.fdy-avatar-group{display:inline-flex;}
|
|
12
12
|
.fdy-avatar-group .fdy-avatar{border:2px solid var(--color-surface);margin-left:calc(var(--space-2)*-1);}
|
|
13
13
|
.fdy-avatar-group .fdy-avatar:first-child{margin-left:0;}
|
|
14
|
-
/* Decorative identity tones
|
|
14
|
+
/* Decorative identity tones, a soft tint of a categorical chart colour with a theme-aware,
|
|
15
15
|
* text-leaning foreground so initials keep WCAG AA (verified >=4.5:1 in light & dark by
|
|
16
16
|
* test/contrast.test.mjs). Use to disambiguate same-initial avatars (e.g. many "PT …" names):
|
|
17
17
|
* hash a stable 1..8 index off the FULL name in your app, then apply .fdy-avatar--tone-<n>. */
|
package/src/components/badge.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Badge */
|
|
2
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 color-mix(in srgb,var(--color-primary) 22%,transparent);box-shadow:0 1px 1.5px rgba(16,14,30,.05);}
|
|
3
3
|
.fdy-badge--success{color:var(--color-success-strong);background:var(--color-success-soft);border-color:color-mix(in srgb,var(--color-success) 26%,transparent);}
|
|
4
4
|
.fdy-badge--warning{color:var(--color-warning-strong);background:var(--color-warning-soft);border-color:color-mix(in srgb,var(--color-warning) 26%,transparent);}
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
.fdy-badge--info{color:var(--color-info-strong);background:var(--color-info-soft);border-color:color-mix(in srgb,var(--color-info) 26%,transparent);}
|
|
7
7
|
.fdy-badge--outline{background:transparent;border-color:var(--color-border-strong);color:var(--color-text-muted);box-shadow:none;}
|
|
8
8
|
|
|
9
|
-
/* Categorical tone badges
|
|
9
|
+
/* Categorical tone badges, a status vocabulary larger than the semantic palette, from the
|
|
10
10
|
* general --tone-1..8 tokens. Same validated AA formula as .fdy-avatar--tone-* and
|
|
11
11
|
* .fdy-chip--tone-* (text >=4.5:1 both themes, gated by test/contrast.test.mjs).
|
|
12
12
|
*
|
|
13
13
|
* Semantics come first: a state that IS good, bad or waiting takes --success / --danger /
|
|
14
|
-
* --warning. These are for the rest
|
|
14
|
+
* --warning. These are for the rest, the states a workflow distinguishes but a palette of five
|
|
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. */
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
.fdy-badge--tone-7{--_fdy-badge-tone:var(--tone-7);}
|
|
26
26
|
.fdy-badge--tone-8{--_fdy-badge-tone:var(--tone-8);}
|
|
27
27
|
|
|
28
|
-
/* Overlay badge
|
|
28
|
+
/* Overlay badge, anchors a small count/dot/icon badge to a corner of any element
|
|
29
29
|
* (icon, button, text). Wrap the content in .fdy-badge-wrap; add .fdy-badge-ov as a
|
|
30
30
|
* sibling. Default is a solid neutral pill; colour + dot + position via modifiers. */
|
|
31
31
|
.fdy-badge-wrap{position:relative;display:inline-flex;vertical-align:middle;}
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
/* Solid colours safe for count/text (on-* tokens, AA in light & dark) */
|
|
36
36
|
.fdy-badge-ov--primary{background:var(--color-primary);color:var(--color-on-primary);}
|
|
37
37
|
.fdy-badge-ov--danger{background:var(--color-danger-btn);color:var(--color-on-danger);}
|
|
38
|
-
/* Dot-oriented accents (for dots
|
|
38
|
+
/* Dot-oriented accents (for dots, plain text on these is not guaranteed AA) */
|
|
39
39
|
.fdy-badge-ov--accent{background:var(--color-accent);}
|
|
40
40
|
.fdy-badge-ov--info{background:var(--color-info);}
|
|
41
41
|
.fdy-badge-ov--success{background:var(--color-success);}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Breadcrumb */
|
|
2
2
|
.fdy-breadcrumb{font-size:var(--text-sm);}
|
|
3
3
|
.fdy-breadcrumb__list{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2);list-style:none;margin:0;padding:0;}
|
|
4
4
|
.fdy-breadcrumb__item{display:inline-flex;align-items:center;gap:var(--space-2);color:var(--color-text-muted);}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Responsive display utilities (pairs with freeday-breakpoint.js).
|
|
2
2
|
* Breakpoint scale (min-width, px): sm 600 · md 960 · lg 1280 · xl 1920.
|
|
3
|
-
* NOTE: media-query breakpoints must be literal
|
|
3
|
+
* NOTE: media-query breakpoints must be literal. CSS can't read custom properties
|
|
4
4
|
* inside @media, so these px are structural, not stray magic numbers. */
|
|
5
5
|
.fdy-hidden{display:none !important;}
|
|
6
6
|
|
|
7
|
-
/* hide-below-X
|
|
7
|
+
/* hide-below-X, hidden when the viewport is narrower than X (content shows at X and up) */
|
|
8
8
|
@media (max-width:599.98px){.fdy-hide-below-sm{display:none !important;}}
|
|
9
9
|
@media (max-width:959.98px){.fdy-hide-below-md{display:none !important;}}
|
|
10
10
|
@media (max-width:1279.98px){.fdy-hide-below-lg{display:none !important;}}
|
|
11
11
|
|
|
12
|
-
/* hide-above-X
|
|
12
|
+
/* hide-above-X, hidden when the viewport is X and wider (content shows below X) */
|
|
13
13
|
@media (min-width:600px){.fdy-hide-above-sm{display:none !important;}}
|
|
14
14
|
@media (min-width:960px){.fdy-hide-above-md{display:none !important;}}
|
|
15
15
|
@media (min-width:1280px){.fdy-hide-above-lg{display:none !important;}}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Button */
|
|
2
2
|
.fdy-btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);
|
|
3
3
|
height:var(--control-h);padding:0 var(--space-4);
|
|
4
4
|
font-family:var(--font-body);font-size:var(--text-sm);font-weight:var(--weight-semibold);
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
.fdy-btn:active{transform:translateY(1px);box-shadow:0 1px 4px -1px color-mix(in srgb,var(--color-primary) 46%,transparent);}
|
|
12
12
|
.fdy-btn:disabled{opacity:.5;cursor:not-allowed;transform:none;box-shadow:none;}
|
|
13
13
|
/* Pressed / toggle. `aria-pressed` is already the right attribute for a toggle button, so this adds
|
|
14
|
-
no class and no new markup contract
|
|
14
|
+
no class and no new markup contract, and it is what makes .fdy-btn-group a complete segmented
|
|
15
15
|
control: the group joins the borders, this says which segment you are on. Each fill variant
|
|
16
16
|
defines its OWN pressed look below; a single shared rule cannot work, because a gradient is a
|
|
17
17
|
background-IMAGE and would blank the ghost variant's background-color. Solid: invert the gradient
|
|
@@ -23,12 +23,12 @@
|
|
|
23
23
|
.fdy-btn--ghost:hover{transform:none;background:var(--color-primary-soft);border-color:var(--color-primary-border);box-shadow:var(--shadow-1);}
|
|
24
24
|
.fdy-btn--ghost:active{transform:translateY(1px);}
|
|
25
25
|
|
|
26
|
-
/* --danger used to be the SOLID treatment and nothing else, so `fdy-btn--ghost fdy-btn--danger
|
|
27
|
-
which every reader parses as "quiet destructive"
|
|
26
|
+
/* --danger used to be the SOLID treatment and nothing else, so `fdy-btn--ghost fdy-btn--danger`,
|
|
27
|
+
which every reader parses as "quiet destructive", rendered a second SOLID button beside Save.
|
|
28
28
|
That is a hierarchy defect, not a cosmetic one: the base class is already the one primary per
|
|
29
29
|
screen. `.fdy-menu__item--danger` has always modelled the quiet form (red text, not a red block);
|
|
30
30
|
the pairing rules below bring it to the button. They are more specific than this one and come
|
|
31
|
-
after it, so this rule needs no :not() guard of its own
|
|
31
|
+
after it, so this rule needs no :not() guard of its own, one mechanism, not two. */
|
|
32
32
|
.fdy-btn--danger{background:linear-gradient(180deg,var(--color-danger-btn),color-mix(in srgb,#000 12%,var(--color-danger-btn)));color:var(--color-on-danger);box-shadow:0 2px 9px -1px color-mix(in srgb,var(--color-danger-btn) 46%,transparent),inset 0 1px 0 rgba(255,255,255,.22);}
|
|
33
33
|
.fdy-btn--danger:hover{transform:translateY(-1px);box-shadow:0 6px 18px -2px color-mix(in srgb,var(--color-danger-btn) 54%,transparent),inset 0 1px 0 rgba(255,255,255,.22);}
|
|
34
34
|
.fdy-btn--danger:active{transform:translateY(1px);}
|
|
@@ -42,21 +42,21 @@
|
|
|
42
42
|
.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
43
|
.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);}
|
|
44
44
|
|
|
45
|
-
/* Icon inside a button
|
|
45
|
+
/* Icon inside a button, scales with the button's font size (works for text+icon & icon-only) */
|
|
46
46
|
.fdy-btn__icon{display:inline-flex;flex:none;}
|
|
47
47
|
.fdy-btn__icon svg{display:block;width:1.15em;height:1.15em;}
|
|
48
48
|
|
|
49
|
-
/* Text button
|
|
49
|
+
/* Text button, lowest emphasis (no fill/border), colored label, subtle hover tint */
|
|
50
50
|
.fdy-btn--text{background:none;color:var(--color-primary-strong);border-color:transparent;box-shadow:none;}
|
|
51
51
|
.fdy-btn--text:hover{transform:none;background:var(--color-primary-soft);box-shadow:none;}
|
|
52
52
|
.fdy-btn--text:active{transform:translateY(1px);box-shadow:none;}
|
|
53
|
-
/* Ghost/text pressed: the kit's selected-surface pair (soft fill + strong ink)
|
|
53
|
+
/* Ghost/text pressed: the kit's selected-surface pair (soft fill + strong ink), the same pairing
|
|
54
54
|
.fdy-nav__item uses for the current page, and one the AA contrast gate already covers. This is
|
|
55
55
|
the realistic segmented control: a row of --ghost buttons with exactly one aria-pressed. */
|
|
56
56
|
.fdy-btn--ghost[aria-pressed="true"],.fdy-btn--text[aria-pressed="true"]{background:var(--color-primary-soft);color:var(--color-primary-strong);border-color:var(--color-primary-border);box-shadow:none;}
|
|
57
57
|
.fdy-btn--ghost[aria-pressed="true"]:hover,.fdy-btn--text[aria-pressed="true"]:hover{background:var(--color-primary-soft);transform:none;box-shadow:none;}
|
|
58
58
|
|
|
59
|
-
/* Stretched hit area (#007)
|
|
59
|
+
/* Stretched hit area (#007), spread this button's click target over its nearest positioned
|
|
60
60
|
* ancestor, so "one card, one primary action, one escape hatch" needs no click handler on the
|
|
61
61
|
* <article>: the real control stays a real <button>/<a> and keeps its keyboard semantics.
|
|
62
62
|
* `.fdy-card` is already position:relative, so it is the anchor.
|
|
@@ -65,19 +65,19 @@
|
|
|
65
65
|
* a transformed element becomes the containing block for its own absolutely-positioned descendants,
|
|
66
66
|
* so `inset:0` re-anchors from the card to the button's own box the moment the button is hovered or
|
|
67
67
|
* pressed. Measured: mousedown lands on the button, the overlay collapses under the pointer, mouseup
|
|
68
|
-
* lands on some other element, and the browser fires `click` on their common ancestor
|
|
68
|
+
* lands on some other element, and the browser fires `click` on their common ancestor, the button
|
|
69
69
|
* never gets one. It demos fine on hover for --text/--ghost (their hover is already transform:none)
|
|
70
70
|
* and only a real press reveals it; the default and --danger fills break one step earlier, on hover.
|
|
71
71
|
* Press feedback moves to the card, which is the object actually being pressed (see card.css). */
|
|
72
72
|
.fdy-btn--stretch::after{content:'';position:absolute;inset:0;}
|
|
73
73
|
.fdy-btn--stretch:hover,.fdy-btn--stretch:active{transform:none;}
|
|
74
74
|
|
|
75
|
-
/* Icon-only
|
|
75
|
+
/* Icon-only, square; combine with --ghost/--text/--sm/--lg. Requires aria-label. */
|
|
76
76
|
.fdy-btn--icon{width:var(--control-h);padding:0;}
|
|
77
77
|
.fdy-btn--icon.fdy-btn--sm{width:calc(var(--control-h) - var(--space-2));}
|
|
78
78
|
.fdy-btn--icon.fdy-btn--lg{width:calc(var(--control-h) + var(--space-2));}
|
|
79
79
|
|
|
80
|
-
/* Button group
|
|
80
|
+
/* Button group, segmented, joined borders, rounded only at the ends */
|
|
81
81
|
.fdy-btn-group{display:inline-flex;}
|
|
82
82
|
.fdy-btn-group .fdy-btn{border-radius:0;}
|
|
83
83
|
.fdy-btn-group .fdy-btn:not(:first-child){margin-left:calc(var(--bw) * -1);}
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
.fdy-btn-group .fdy-btn:first-child{border-top-left-radius:var(--radius-md);border-bottom-left-radius:var(--radius-md);}
|
|
86
86
|
.fdy-btn-group .fdy-btn:last-child{border-top-right-radius:var(--radius-md);border-bottom-right-radius:var(--radius-md);}
|
|
87
87
|
|
|
88
|
-
/* FAB
|
|
88
|
+
/* FAB, circular elevated action button (icon), optional extended pill with label */
|
|
89
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:linear-gradient(180deg,var(--color-primary),color-mix(in srgb,#000 10%,var(--color-primary)));box-shadow:var(--shadow-3);transition:transform var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
|
|
90
90
|
.fdy-fab:hover{transform:translateY(-1px);box-shadow:var(--shadow-4);}
|
|
91
91
|
.fdy-fab:active{transform:translateY(1px);}
|
package/src/components/card.css
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
/* Freeday
|
|
2
|
-
/* position:relative
|
|
1
|
+
/* Freeday. Card */
|
|
2
|
+
/* position:relative, containing block for out-of-flow card content (see base.css); it also gives
|
|
3
3
|
consumers the anchor a corner ribbon/badge needs. */
|
|
4
4
|
.fdy-card{position:relative;background:var(--color-surface);border:var(--bw) solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;box-shadow:var(--shadow-lift);transition:box-shadow var(--dur-base) var(--ease-standard),transform var(--dur-base) var(--ease-standard);}
|
|
5
5
|
.fdy-card--elevated{box-shadow:var(--shadow-lift-hover);}
|
|
6
6
|
.fdy-card--interactive{cursor:pointer;}
|
|
7
7
|
.fdy-card--interactive:hover{box-shadow:var(--shadow-lift-hover);transform:translateY(-3px);}
|
|
8
|
-
/* Pressing a stretched target (see .fdy-btn--stretch) is pressing the CARD
|
|
8
|
+
/* Pressing a stretched target (see .fdy-btn--stretch) is pressing the CARD, the button's own
|
|
9
9
|
nudge had to go, and the object under the pointer is the card. Ordered before the reduced-motion
|
|
10
10
|
block and the disabled rules so both still win at equal specificity. */
|
|
11
11
|
.fdy-card--interactive:has(.fdy-btn--stretch:active){transform:translateY(-1px);}
|
|
12
12
|
/* The global reduced-motion reset kills the ANIMATION; drop the -3px shift too so the card
|
|
13
13
|
* doesn't jump. The shadow still communicates the affordance. */
|
|
14
14
|
@media (prefers-reduced-motion:reduce){.fdy-card--interactive:hover,.fdy-card--interactive:has(.fdy-btn--stretch:active){transform:none;}}
|
|
15
|
-
/* When the whole card IS the control
|
|
16
|
-
* announced
|
|
15
|
+
/* When the whole card IS the control, a real <button> so the row is keyboard-reachable and
|
|
16
|
+
* announced, reset the UA button box. Note: it does NOT reset background/border, because
|
|
17
17
|
* .fdy-card already sets its own surface + border (resetting them here would strip the card). */
|
|
18
18
|
.fdy-card--button{display:block;width:100%;text-align:inherit;color:inherit;appearance:none;-webkit-appearance:none;}
|
|
19
|
-
/* Disabled card-as-control
|
|
19
|
+
/* Disabled card-as-control, same contract as .fdy-list__row--button: dim + not-allowed, and the
|
|
20
20
|
--interactive lift is withdrawn so a refusing control stops answering the pointer. */
|
|
21
21
|
.fdy-card--button:disabled,.fdy-card--button[aria-disabled="true"],.fdy-card--interactive[aria-disabled="true"]{opacity:.5;cursor:not-allowed;}
|
|
22
22
|
.fdy-card--interactive:disabled:hover,.fdy-card--interactive[aria-disabled="true"]:hover{box-shadow:var(--shadow-lift);transform:none;}
|
|
23
|
-
/* Escape hatches stay clickable above a stretched target
|
|
23
|
+
/* Escape hatches stay clickable above a stretched target, automatic, not a documented convention,
|
|
24
24
|
because forgetting it fails SILENTLY: the secondary control looks and hovers exactly as before and
|
|
25
25
|
simply never receives the click, which is the very symptom the pattern exists to cure. Matched by
|
|
26
26
|
shape (anything focusable) so a control the kit does not ship yet is covered too. */
|
|
27
27
|
:is(.fdy-card,.fdy-list__row):has(.fdy-btn--stretch) :is(a[href],button,input,select,textarea,[tabindex]):not(.fdy-btn--stretch){z-index:1;}
|
|
28
28
|
/* The position is a DEFAULT, not an override: z-index needs a positioned box (or a flex/grid item),
|
|
29
|
-
but an app that deliberately pins a control inside the card
|
|
29
|
+
but an app that deliberately pins a control inside the card, a corner dismiss, a favourite star,
|
|
30
30
|
must keep its own `position:absolute`. Wrapped in :where() so this whole selector weighs nothing
|
|
31
31
|
and ANY app rule wins it; measured, because at normal specificity the kit silently dragged an
|
|
32
32
|
absolutely positioned dismiss button back into the flow. Do not merge these two rules. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Carousel (scroll-snap track + arrows + dots). Enhanced by freeday-carousel.js. */
|
|
2
2
|
.fdy-carousel{position:relative;}
|
|
3
|
-
/* position:relative
|
|
3
|
+
/* position:relative means the arrows are positioned against .fdy-carousel (the parent), so this only
|
|
4
4
|
catches out-of-flow content inside the SLIDES, which would otherwise escape the viewport
|
|
5
5
|
entirely and scroll the page by one slide-offset per slide (see base.css). */
|
|
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;}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Cascade select (hierarchical drill-down picker).
|
|
2
2
|
* Built by freeday-cascade.js from a [data-fdy-cascade] wrapper whose hidden
|
|
3
3
|
* nested <ul> is the data model: a <li> with a child <ul> is a branch, one
|
|
4
|
-
* without is a selectable leaf. The popover shows one level at a time
|
|
4
|
+
* without is a selectable leaf. The popover shows one level at a time, branches
|
|
5
5
|
* drill in, a back control ascends, a leaf selects (value = full path).
|
|
6
6
|
* Trigger visual matches the combo / date / time control family. */
|
|
7
7
|
.fdy-cascade{position:relative;display:inline-block;width:100%;max-width:22rem;}
|
package/src/components/cfl.css
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Choose-from-list dialog (searchable master-data picker).
|
|
2
2
|
* Built on .fdy-modal: header + search + scrollable results table + footer.
|
|
3
3
|
* Backs a choose-from-list field (readonly input + .fdy-input-group__btn magnifier). */
|
|
4
4
|
.fdy-modal--cfl{width:min(46rem,calc(100vw - var(--space-8)));}
|
|
5
5
|
.fdy-modal--cfl .fdy-modal__body{padding:0;display:flex;flex-direction:column;min-height:0;overflow:hidden;}
|
|
6
6
|
|
|
7
|
-
/* Search bar
|
|
7
|
+
/* Search bar, single leading-icon field; Enter/typing filters the results. */
|
|
8
8
|
.fdy-cfl__search{flex:none;padding:var(--space-4) var(--space-5);border-bottom:var(--bw) solid var(--color-border-muted);}
|
|
9
9
|
|
|
10
|
-
/* Results
|
|
10
|
+
/* Results, dense rows, sticky header, its own scroll region. */
|
|
11
11
|
.fdy-cfl__results{overflow:auto;max-height:min(24rem,50vh);}
|
|
12
12
|
.fdy-cfl__results .fdy-table th{position:sticky;top:0;z-index:1;}
|
|
13
13
|
.fdy-cfl__row{cursor:pointer;}
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
.fdy-cfl__empty{padding:var(--space-8) var(--space-5);text-align:center;color:var(--color-text-muted);font-size:var(--text-sm);margin:0;}
|
|
23
23
|
.fdy-cfl__empty[hidden]{display:none;}
|
|
24
24
|
|
|
25
|
-
/* Footer
|
|
25
|
+
/* Footer, selection count (multi) sits left, actions right. */
|
|
26
26
|
.fdy-modal--cfl .fdy-modal__footer{justify-content:space-between;}
|
|
27
27
|
.fdy-cfl__count{font-size:var(--text-sm);color:var(--color-text-muted);}
|
|
28
28
|
.fdy-cfl__actions{display:flex;gap:var(--space-2);}
|
package/src/components/chart.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Charts (zero-dependency CSS/SVG, rendered by freeday-chart.js).
|
|
2
2
|
* Colour comes from --fdy-chart (a token, set inline by the renderer from data-fdy-color). */
|
|
3
3
|
|
|
4
4
|
/* Sparkline / trend line (SVG) */
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
/* Hover emphasis: dim all bars while the chart is hovered, keep the hovered one lit. */
|
|
19
19
|
@media (hover:hover){.fdy-bars:hover .fdy-bars__bar{opacity:.4;}.fdy-bars__col:hover .fdy-bars__bar{opacity:1;}}
|
|
20
20
|
|
|
21
|
-
/* Interactive tooltip (shared by bar + donut)
|
|
21
|
+
/* Interactive tooltip (shared by bar + donut). Chart.js-style hover readout. */
|
|
22
22
|
.fdy-chart__tip{position:absolute;z-index:5;left:0;top:0;pointer-events:none;transform:translate(-50%,calc(-100% - 8px));background:var(--color-inverse-surface);color:var(--color-inverse-text);font-size:var(--text-xs);font-weight:var(--weight-medium);font-variant-numeric:tabular-nums;padding:var(--space-1) var(--space-2);border-radius:var(--radius-sm);white-space:nowrap;box-shadow:var(--shadow-2);opacity:0;transition:opacity var(--dur-fast) var(--ease-standard);}
|
|
23
23
|
.fdy-chart__tip.is-visible{opacity:1;}
|
|
24
24
|
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
/* Horizontal legend for cartesian charts (legend sits above the plot). */
|
|
40
40
|
.fdy-chart__legend--row{flex-direction:row;flex-wrap:wrap;gap:var(--space-2) var(--space-4);}
|
|
41
41
|
|
|
42
|
-
/* Cartesian charts (line / area / multi-series & stacked bar)
|
|
42
|
+
/* Cartesian charts (line / area / multi-series & stacked bar). SVG with themed axes.
|
|
43
43
|
* Axis colour comes from --chart-grid / --chart-tick (both theme-aware); series colours are
|
|
44
44
|
* set inline by the renderer from --chart-1..8, so the whole chart recolours on data-theme. */
|
|
45
45
|
.fdy-chart-xy{position:relative;display:flex;flex-direction:column;gap:var(--space-3);width:100%;--fdy-chart-tick-size:var(--text-xs);}
|
package/src/components/chip.css
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/* Freeday
|
|
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
3
|
.fdy-chip--primary{color:var(--color-primary-strong);background:var(--color-primary-soft);border-color:color-mix(in srgb,var(--color-primary) 22%,transparent);}
|
|
4
|
-
/* Categorical tone chips
|
|
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
7
|
.fdy-chip--tone-1,.fdy-chip--tone-2,.fdy-chip--tone-3,.fdy-chip--tone-4,.fdy-chip--tone-5,.fdy-chip--tone-6,.fdy-chip--tone-7,.fdy-chip--tone-8{background:color-mix(in srgb,var(--_fdy-chip-tone) 18%,var(--color-surface));color:color-mix(in srgb,var(--_fdy-chip-tone) 50%,var(--color-text));border-color:color-mix(in srgb,var(--_fdy-chip-tone) 30%,var(--color-border));}
|