@cahyo-dimas/freeday 1.54.0 → 2.1.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 +713 -545
- package/COMPONENTS.md +417 -139
- package/README.id.md +46 -45
- package/README.md +46 -43
- package/USAGE.md +34 -34
- package/adapters/blazor/FdyAppShell.razor +2 -2
- package/adapters/blazor/FdyAppShell.razor.cs +6 -6
- 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 +1 -1
- package/adapters/core/app-shell.js +6 -6
- package/adapters/core/cfl-value.d.ts +11 -0
- package/adapters/core/cfl-value.js +31 -0
- package/adapters/core/table-model.d.ts +2 -2
- package/adapters/core/table-model.js +8 -8
- package/adapters/react/components/FdyAppShell.tsx +5 -5
- 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 +4 -1
- package/adapters/react/index.js +1 -0
- package/adapters/react/useFreeday.js +3 -3
- package/adapters/vue/components/FdyAppShell.vue +5 -5
- 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 +5 -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 +9 -9
- package/dist/freeday-autocomplete.js +2 -2
- package/dist/freeday-breakpoint.js +1 -1
- package/dist/freeday-carousel.js +8 -7
- package/dist/freeday-cascade.js +10 -9
- package/dist/freeday-cfl.js +12 -11
- 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 +17 -16
- package/dist/freeday-mask.js +9 -8
- 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 +10 -9
- package/dist/freeday-table.js +18 -17
- package/dist/freeday-tabs.js +1 -1
- package/dist/freeday-timepicker.js +2 -2
- package/dist/freeday-toast.js +11 -10
- package/dist/freeday-tree.js +3 -3
- package/dist/freeday-upload.js +28 -27
- package/dist/freeday.bundle.css +176 -176
- package/dist/freeday.css +170 -170
- package/dist/freeday.d.ts +2 -2
- package/dist/freeday.js +189 -176
- package/dist/freeday.tokens.css +5 -5
- package/docs/agent-onboarding.md +49 -46
- package/docs/getting-started.md +49 -40
- package/docs/integrations.md +18 -18
- package/docs/reference-screen.html +14 -14
- package/package.json +4 -3
- 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
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));}
|
package/src/components/combo.css
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Combobox (custom styled select, WAI-ARIA APG select-only) */
|
|
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
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 color-mix(in srgb,var(--color-primary) 26%,transparent);}
|
|
6
6
|
.fdy-combo__button.is-open{border-color:var(--color-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
|
|
7
7
|
.fdy-combo__button:disabled,.fdy-combo__button[aria-disabled="true"]{opacity:.5;cursor:not-allowed;}
|
|
8
|
-
/* Read-only: stays focusable & shows its value at full contrast but can't open
|
|
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). */
|
|
10
10
|
.fdy-combo__button[aria-readonly="true"]{background:var(--color-surface-2);border-color:var(--color-border);cursor:default;}
|
|
11
11
|
.fdy-combo__button[aria-readonly="true"]:hover{border-color:var(--color-border);}
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
.fdy-combo__option.is-highlighted[aria-selected="true"]{box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--color-primary) 55%,transparent);}
|
|
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
|
-
was read twice
|
|
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. */
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Page composition & type roles.
|
|
2
2
|
*
|
|
3
3
|
* The kit stops at the component; these primitives encode *how a page is assembled* so screens
|
|
4
4
|
* built by different people cohere. They carry the usage doctrine (see USAGE.md) in markup: a
|
|
5
|
-
* named type role gets used, a doc gets skimmed. Everything here is token-driven layout
|
|
5
|
+
* named type role gets used, a doc gets skimmed. Everything here is token-driven layout, no new
|
|
6
6
|
* colours, no new shadows. Pair with .fdy-app (the shell) and the components inside.
|
|
7
7
|
*/
|
|
8
8
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* page title (the mistake that collapses three levels into one grey mush). */
|
|
12
12
|
/* --color-text-muted, not -subtle: an eyebrow is a small label naming the thing below it, i.e.
|
|
13
13
|
prose. -subtle is gated at 3.0 on purpose (placeholders, separators, decorative glyphs) and
|
|
14
|
-
measures 4.41 on the page ground
|
|
14
|
+
measures 4.41 on the page ground, axe reports it as a serious color-contrast failure. Same
|
|
15
15
|
argument, same ink, same 4.41 as .fdy-stat__label in 1.30.0. */
|
|
16
16
|
.fdy-eyebrow{font-family:var(--font-body);font-size:var(--text-xs);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-wide);text-transform:uppercase;color:var(--color-text-muted);margin:0;}
|
|
17
17
|
.fdy-title-page{font-family:var(--font-display);font-size:var(--text-3xl);font-weight:var(--weight-bold);letter-spacing:var(--tracking-tighter);line-height:var(--leading-tight);color:var(--color-text);margin:0;text-wrap:balance;}
|
|
@@ -45,19 +45,19 @@
|
|
|
45
45
|
.fdy-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));gap:var(--space-5);}
|
|
46
46
|
.fdy-stat{display:flex;flex-direction:column;gap:var(--space-1);min-width:0;}
|
|
47
47
|
/* --color-text-muted, not -subtle: this is a LABEL, i.e. text, and -subtle is deliberately gated at
|
|
48
|
-
3.0 (placeholders, dividers, decorative glyphs). On surface-2 -subtle measures 4.41
|
|
48
|
+
3.0 (placeholders, dividers, decorative glyphs). On surface-2 -subtle measures 4.41, under AA
|
|
49
49
|
for text, and a stat label is often the only thing naming the number above it. */
|
|
50
50
|
.fdy-stat__label{font-size:var(--text-xs);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-wide);text-transform:uppercase;color:var(--color-text-muted);}
|
|
51
51
|
.fdy-stat__value{font-family:var(--font-display);font-size:var(--text-3xl);font-weight:var(--weight-bold);letter-spacing:var(--tracking-tighter);line-height:1;color:var(--color-text);font-variant-numeric:tabular-nums;}
|
|
52
52
|
.fdy-stat__value small{font-size:var(--text-lg);font-weight:var(--weight-semibold);color:var(--color-text-muted);}
|
|
53
53
|
.fdy-stat__meta{font-size:var(--text-sm);color:var(--color-text-muted);display:flex;align-items:center;gap:var(--space-2);}
|
|
54
|
-
/* A stat value is display type sized for a COUNT
|
|
54
|
+
/* A stat value is display type sized for a COUNT, "1,284", and a back office puts money in it.
|
|
55
55
|
"IDR 300,000.00" needs 224px at --text-3xl and the grid's own track is 11rem, so it wrapped
|
|
56
56
|
between the currency and the number: two lines of 31px where the design says one. Measured, not
|
|
57
57
|
guessed (#020).
|
|
58
58
|
The value shrinks only when its own column is too narrow to hold it, so a wide dashboard keeps
|
|
59
59
|
exactly the type it has today. Scoped to a stat INSIDE .fdy-stats, where the width comes from the
|
|
60
|
-
grid track
|
|
60
|
+
grid track, `container-type:inline-size` on a standalone .fdy-stat would stop it sizing to its
|
|
61
61
|
own content. The unconditional rule above stays as the fallback wherever @container is missing. */
|
|
62
62
|
@supports (container-type:inline-size){
|
|
63
63
|
.fdy-stats>.fdy-stat{container-type:inline-size;}
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
/* Icon box (#001 §4). The kit ships no icons on purpose, but every icon slot it DOES ship
|
|
71
71
|
(.fdy-btn__icon, .fdy-nav__icon, .fdy-state__icon) assumes something sensible inside, so a
|
|
72
72
|
standalone icon had no contract and every app invented one. 1em keeps the glyph matched to the
|
|
73
|
-
text beside it at every scale
|
|
73
|
+
text beside it at every scale, bring your own paths, use our box. Works on the <svg> itself or
|
|
74
74
|
on a wrapper around it. */
|
|
75
75
|
.fdy-icon{display:inline-block;width:1em;height:1em;flex:none;vertical-align:-0.125em;}
|
|
76
76
|
.fdy-icon>svg{display:block;width:100%;height:100%;}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Date picker (input-styled trigger + calendar popover).
|
|
2
2
|
* Built by freeday-datepicker.js from an empty [data-fdy-datepicker] wrapper. */
|
|
3
3
|
.fdy-datepicker{position:relative;display:inline-block;width:100%;max-width:14rem;}
|
|
4
4
|
/* Inside a labelled field, a picker fills the field like every other control.
|
|
5
5
|
The 14rem/11rem caps above suit a picker standing on its own; in a form grid
|
|
6
|
-
they leave the date box visibly short of the combo beside it
|
|
7
|
-
319px in a two-column layout
|
|
6
|
+
they leave the date box visibly short of the combo beside it. 224px against
|
|
7
|
+
319px in a two-column layout, and the row reads as ragged. The field's own
|
|
8
8
|
max-width is what should govern there. */
|
|
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);}
|
|
@@ -50,12 +50,12 @@
|
|
|
50
50
|
.fdy-cal__nav:hover{background:var(--color-surface-2);color:var(--color-text);}
|
|
51
51
|
.fdy-cal__nav:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
|
|
52
52
|
.fdy-cal__grid{display:grid;grid-template-columns:repeat(7,2rem);gap:2px;}
|
|
53
|
-
/* Month grid
|
|
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.
|
|
55
55
|
|
|
56
56
|
AFTER `.fdy-cal__grid`, and that is the whole rule: the element carries both classes, they weigh
|
|
57
57
|
the same, so source order decides. Declared earlier, the modifier lost to the base it modifies and
|
|
58
|
-
twelve months rendered seven across
|
|
58
|
+
twelve months rendered seven across, the layout the comment above described, never once applied. */
|
|
59
59
|
.fdy-cal__grid--months,.fdy-cal__grid--years{grid-template-columns:repeat(3,1fr);gap:var(--space-1);}
|
|
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;}
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
/* Responsive: each picker may shrink (min-width:0) & wrap so the range never overflows a
|
|
74
74
|
narrow row/phone; inline-flex still hugs to ~23rem at wider widths (look unchanged). */
|
|
75
75
|
.fdy-daterange{display:inline-flex;flex-wrap:wrap;align-items:center;gap:var(--space-2);}
|
|
76
|
-
/* Each picker shrinks from an 11rem basis but not below ~7rem
|
|
76
|
+
/* Each picker shrinks from an 11rem basis but not below ~7rem, enough for "dd/mm/yyyy" + the icon,
|
|
77
77
|
so a squeezed range compresses gracefully (and wraps) instead of clipping the date. */
|
|
78
78
|
.fdy-daterange .fdy-datepicker{flex:0 1 11rem;min-width:7rem;}
|
|
79
79
|
.fdy-daterange__sep{flex:none;color:var(--color-text-subtle);}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Datetime picker (composed: date picker + time picker → one value).
|
|
2
2
|
* A thin composer (freeday-datetime.js) links a child [data-fdy-datepicker] and
|
|
3
3
|
* [data-fdy-timepicker] and emits a single combined event. Icon variants come from
|
|
4
4
|
* the child pickers (data-fdy-no-icon / <template data-fdy-icon>). */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Description list (key-value detail view) */
|
|
2
2
|
.fdy-dl{display:grid;grid-template-columns:auto 1fr;gap:var(--space-2) var(--space-5);margin:0;}
|
|
3
3
|
.fdy-dl dt{color:var(--color-text-muted);font-size:var(--text-sm);}
|
|
4
4
|
.fdy-dl dd{margin:0;color:var(--color-text);font-size:var(--text-sm);font-weight:var(--weight-medium);}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Divider */
|
|
2
2
|
.fdy-divider{border:0;border-top:var(--bw) solid var(--color-border);margin:var(--space-4) 0;}
|
|
3
3
|
.fdy-divider--vertical{border-top:0;border-left:var(--bw) solid var(--color-border);margin:0 var(--space-4);align-self:stretch;}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Drawer (temporary side panel on native <dialog>: focus-trap / Esc /
|
|
2
2
|
* backdrop / return-focus are free). Anchors left (default) or right.
|
|
3
3
|
* Enhanced by freeday-drawer.js (open trigger + backdrop-click close). */
|
|
4
4
|
/* display is gated behind [open] so the native <dialog> stays hidden until opened.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. File upload (drop target + explicit per-file states).
|
|
2
2
|
* Enhanced by freeday-upload.js; states: idle · drag-over · uploading · done · error. */
|
|
3
3
|
.fdy-dropzone{display:flex;flex-direction:column;align-items:center;gap:var(--space-2);padding:var(--space-6);text-align:center;border:2px dashed var(--color-control-border);border-radius:var(--radius-lg);background:var(--color-surface-2);color:var(--color-text-muted);cursor:pointer;transition:border-color var(--dur-fast) var(--ease-standard),background var(--dur-fast) var(--ease-standard),color var(--dur-fast) var(--ease-standard);}
|
|
4
4
|
.fdy-dropzone:hover{border-color:var(--color-text-muted);}
|
|
@@ -10,17 +10,17 @@
|
|
|
10
10
|
.fdy-dropzone__icon svg{display:block;width:2rem;height:2rem;}
|
|
11
11
|
.fdy-dropzone__title{font-size:inherit;font-weight:var(--weight-medium);color:var(--color-text);margin:0;}
|
|
12
12
|
.fdy-dropzone__hint{font-size:var(--text-xs);}
|
|
13
|
-
/* Wide (horizontal) layout
|
|
13
|
+
/* Wide (horizontal) layout, icon beside the copy instead of stacked, for full-width
|
|
14
14
|
* sections. Wrap the title + hint in .fdy-dropzone__text. */
|
|
15
15
|
.fdy-dropzone--row{flex-direction:row;align-items:center;justify-content:center;text-align:left;gap:var(--space-4);}
|
|
16
16
|
.fdy-dropzone__text{display:flex;flex-direction:column;gap:var(--space-1);}
|
|
17
17
|
|
|
18
18
|
/* File rows (list of added/uploaded/rejected files) */
|
|
19
|
-
/* Symmetric on purpose. The list carried margin-top and nothing below, so whatever follows it
|
|
20
|
-
an "Add files" button, in every upload UI there is
|
|
19
|
+
/* Symmetric on purpose. The list carried margin-top and nothing below, so whatever follows it,
|
|
20
|
+
an "Add files" button, in every upload UI there is, sat flush against the last row: measured
|
|
21
21
|
0px. A block that claims space on one side only is not spacing, it is a lean. */
|
|
22
22
|
.fdy-filelist{display:flex;flex-direction:column;gap:var(--space-2);margin-top:var(--space-3);margin-bottom:var(--space-3);}
|
|
23
|
-
/* Nothing after it, nothing to separate from
|
|
23
|
+
/* Nothing after it, nothing to separate from, so the room is not spent. */
|
|
24
24
|
.fdy-filelist:last-child{margin-bottom:0;}
|
|
25
25
|
/* Side-by-side rows on wide screens (reflow to one column when narrow). */
|
|
26
26
|
.fdy-filelist--grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(16rem,1fr));}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Filter bar (a consistent row of filters: search + selects + actions).
|
|
2
2
|
* Solves the "ragged filter row": apps improvised flex rows with ad-hoc
|
|
3
3
|
* flex/min-width, so grow fields ballooned while selects stayed tiny and no two
|
|
4
4
|
* screens matched. Here every field snaps to a shared width rhythm; one field may
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
.fdy-filterbar{display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--space-3) var(--space-4);}
|
|
8
8
|
/* Composed with a table toolbar (one element, both classes): the fields decide the alignment.
|
|
9
9
|
* `.fdy-table-toolbar` sets align-items:center (right for bare buttons) at equal specificity, and
|
|
10
|
-
* being later in the bundle it would otherwise win
|
|
10
|
+
* being later in the bundle it would otherwise win, floating labelled fields' neighbours out of line.
|
|
11
11
|
* This 2-class selector (0,2,0) settles it toward the filterbar's flex-end regardless of source order. */
|
|
12
12
|
.fdy-table-toolbar.fdy-filterbar{align-items:flex-end;}
|
|
13
13
|
/* Fields are direct children. Drop the standalone field's 22rem cap; default to the
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
.fdy-filterbar>.fdy-field{max-width:none;flex:0 0 auto;width:12rem;}
|
|
16
16
|
/* Controls fill their field's column (drop the standalone input-group/combo caps). */
|
|
17
17
|
.fdy-filterbar>.fdy-field .fdy-input-group,.fdy-filterbar>.fdy-field .fdy-combo{max-width:none;width:100%;}
|
|
18
|
-
/* Column-width rhythm
|
|
18
|
+
/* Column-width rhythm, pick one per field so bars across the app line up. */
|
|
19
19
|
.fdy-filterbar>.fdy-field--w-sm{width:9rem;}
|
|
20
20
|
.fdy-filterbar>.fdy-field--w-lg{width:15rem;}
|
|
21
21
|
.fdy-filterbar>.fdy-field--w-xl{width:18rem;}
|
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
.fdy-filterbar>.fdy-field--w-grow{flex:1 1 16rem;width:auto;min-width:12rem;}
|
|
26
26
|
/* Trailing actions (buttons) pushed to the end, aligned with the controls' baseline.
|
|
27
27
|
* Wrap note: a wrapped bar breaks flex lines on each item's *flex-basis* (the grow field's
|
|
28
|
-
* `flex:1 1 16rem` reserves 16rem for line-breaking), NOT its rendered width
|
|
28
|
+
* `flex:1 1 16rem` reserves 16rem for line-breaking), NOT its rendered width, so the grow basis,
|
|
29
29
|
* not the visible width, decides the wrap point. Margins can't keep the actions off a lonely wrapped
|
|
30
30
|
* row; to avoid stranded actions, tune the --w-grow basis or drop a field so the bar fits one row. */
|
|
31
|
-
/* A control that carries NO label of its own
|
|
32
|
-
button
|
|
31
|
+
/* A control that carries NO label of its own, a checkbox, a switch, a lone
|
|
32
|
+
button, in a bar whose `align-items:flex-end` exists to line up LABELLED
|
|
33
33
|
fields. Flex-end puts such a control's bottom on the inputs' bottom, so a
|
|
34
34
|
20px checkbox sits well below the 32px input's centre line and reads as
|
|
35
35
|
dropped. Giving it the height of a control lands its box exactly where an
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
line. Raised from IDU_EMATE_APPL_WEB (#014). */
|
|
38
38
|
.fdy-filterbar>.fdy-check,.fdy-filterbar>.fdy-radio,.fdy-filterbar>.fdy-switch,.fdy-filterbar>.fdy-btn{min-height:var(--control-h);}
|
|
39
39
|
.fdy-filterbar__actions{display:flex;align-items:center;gap:var(--space-2);margin-left:auto;}
|
|
40
|
-
/* Opt-in: sit the actions inline right after the last field instead of at the far edge
|
|
40
|
+
/* Opt-in: sit the actions inline right after the last field instead of at the far edge, so when the
|
|
41
41
|
* bar wraps they read as attached to the filters rather than shoved to the right of a lonely row. */
|
|
42
42
|
.fdy-filterbar--actions-inline .fdy-filterbar__actions{margin-left:0;}
|
|
43
43
|
/* Stack on narrow screens so nothing overflows the row. */
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Form grid (document/header fields laid out in aligned columns).
|
|
2
2
|
* The two-dimensional counterpart to the single-column .fdy-field stack and the
|
|
3
3
|
* one-row .fdy-filterbar: related fields sit in columns that reflow to one on narrow
|
|
4
4
|
* screens. Fields are the standard .fdy-field; a field with --full spans the whole row
|
|
5
|
-
* (e.g. an address or note). Zero JS
|
|
5
|
+
* (e.g. an address or note). Zero JS, pure layout over .fdy-field. */
|
|
6
6
|
.fdy-form-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(14rem,1fr));gap:var(--space-4) var(--space-5);}
|
|
7
7
|
.fdy-form-grid>.fdy-field{max-width:none;}
|
|
8
8
|
.fdy-form-grid>.fdy-field--full{grid-column:1/-1;}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Input group (prefix/suffix addons, e.g. Rp / % / icon) */
|
|
2
2
|
.fdy-input-group{display:flex;align-items:stretch;height:var(--control-h);max-width:22rem;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);overflow:hidden;transition:border-color var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
|
|
3
3
|
.fdy-input-group:hover{border-color:var(--color-text-muted);}
|
|
4
4
|
.fdy-input-group:focus-within{border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
|
|
@@ -11,23 +11,23 @@
|
|
|
11
11
|
.fdy-input-group .fdy-input:focus{box-shadow:none;}
|
|
12
12
|
.fdy-input-group__addon{display:inline-flex;align-items:center;padding:0 var(--space-3);background:var(--color-surface-2);color:var(--color-text-muted);font-size:var(--text-sm);white-space:nowrap;border-right:var(--bw) solid var(--color-border);}
|
|
13
13
|
.fdy-input-group__addon:last-child{border-right:0;border-left:var(--bw) solid var(--color-border);}
|
|
14
|
-
/* Borderless leading icon (e.g. search magnifier)
|
|
14
|
+
/* Borderless leading icon (e.g. search magnifier), self-contained like __btn so it
|
|
15
15
|
centers standalone (no base .fdy-input-group__addon required). */
|
|
16
16
|
.fdy-input-group__addon--icon{display:inline-flex;align-items:center;background:transparent;border:0;padding:0 0 0 var(--space-3);color:var(--color-text-subtle);}
|
|
17
17
|
.fdy-input-group__addon--icon svg{display:block;width:1rem;height:1rem;}
|
|
18
|
-
/* The leading icon already provides the left inset
|
|
18
|
+
/* The leading icon already provides the left inset, don't let the input add its full left padding on top. */
|
|
19
19
|
.fdy-input-group__addon--icon + .fdy-input{padding-left:var(--space-2);}
|
|
20
|
-
/* Trailing action button (e.g. choose-from-list magnifier)
|
|
20
|
+
/* Trailing action button (e.g. choose-from-list magnifier), a real, focusable control. */
|
|
21
21
|
.fdy-input-group__btn{display:inline-flex;align-items:center;justify-content:center;flex:none;width:var(--control-h);align-self:stretch;border:0;border-left:var(--bw) solid var(--color-border);background:var(--color-surface-2);color:var(--color-text-muted);cursor:pointer;transition:background var(--dur-fast) var(--ease-standard),color var(--dur-fast) var(--ease-standard);}
|
|
22
22
|
.fdy-input-group__btn:hover{background:var(--color-surface-3);color:var(--color-text);}
|
|
23
23
|
.fdy-input-group__btn:focus-visible{outline:none;color:var(--color-text);box-shadow:inset 0 0 0 2px var(--color-primary);}
|
|
24
24
|
.fdy-input-group__btn svg{display:block;width:1.1rem;height:1.1rem;}
|
|
25
25
|
/* Leading action button (the "−" of a number stepper): the divider belongs on the other side. */
|
|
26
26
|
.fdy-input-group__btn:first-child{border-left:0;border-right:var(--bw) solid var(--color-border);}
|
|
27
|
-
/* At a bound, or on a disabled/read-only field, the button must look as inert as it acts
|
|
27
|
+
/* At a bound, or on a disabled/read-only field, the button must look as inert as it acts, same dim
|
|
28
28
|
+ not-allowed as .fdy-input:disabled. :hover still matches a disabled button, so it is withdrawn. */
|
|
29
29
|
.fdy-input-group__btn:disabled{opacity:.5;cursor:not-allowed;}
|
|
30
30
|
.fdy-input-group__btn:disabled:hover{background:var(--color-surface-2);color:var(--color-text-muted);}
|
|
31
|
-
/* Error state must win over the neutralizing rules above
|
|
31
|
+
/* Error state must win over the neutralizing rules above, promote to the wrapper. */
|
|
32
32
|
.fdy-input-group:has(.fdy-input--error),.fdy-input-group:has(.fdy-combo--error),.fdy-input-group:has([aria-invalid="true"]){border-color:var(--color-danger);}
|
|
33
33
|
.fdy-input-group:has(.fdy-input--error):focus-within,.fdy-input-group:has(.fdy-combo--error):focus-within,.fdy-input-group:has([aria-invalid="true"]):focus-within{border-color:var(--color-danger);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px color-mix(in srgb,var(--color-danger) 26%,transparent);}
|
package/src/components/input.css
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Field & Input */
|
|
2
2
|
.fdy-field{display:flex;flex-direction:column;gap:var(--space-2);max-width:22rem;}
|
|
3
|
-
/* A grouped field is legitimately a <fieldset> (it needs a <legend>)
|
|
3
|
+
/* A grouped field is legitimately a <fieldset> (it needs a <legend>), e.g. a radio/checkbox group.
|
|
4
4
|
Neutralise the UA groove border / padding / min-inline-size so it lays out identically to the
|
|
5
5
|
<div> form (min-inline-size:min-content would otherwise block the filterbar column widths). */
|
|
6
6
|
fieldset.fdy-field{border:0;margin:0;padding:0;min-inline-size:0;}
|
|
7
|
-
/* The rendered legend is laid out outside the fieldset's flex flow, so `gap` never reaches it
|
|
7
|
+
/* The rendered legend is laid out outside the fieldset's flex flow, so `gap` never reaches it,
|
|
8
8
|
measured 0px between legend and first control. This is the spacing contract a grouped field
|
|
9
9
|
needs; it matches .fdy-field's own gap so the group reads as one rhythm. */
|
|
10
10
|
fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
@@ -22,17 +22,17 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
22
22
|
.fdy-input[readonly]:hover,.fdy-textarea[readonly]:hover{border-color:var(--color-border);}
|
|
23
23
|
.fdy-input[readonly]:focus-visible,.fdy-textarea[readonly]:focus-visible{border-color:var(--color-border-strong);box-shadow:none;}
|
|
24
24
|
/* Numbers (#44): the user agent draws its own spin buttons inside the field, in OS widget colours no
|
|
25
|
-
theme reaches
|
|
25
|
+
theme reaches, on a dark surface they read as a light-grey artefact glued to a themed field, and
|
|
26
26
|
it is the only unthemed control on the page. Removed so .fdy-input looks the same whatever its
|
|
27
27
|
type. `appearance:textfield` is the Firefox half, the pseudo-element the Blink/WebKit half; both
|
|
28
|
-
are needed. This trades away the mouse-only increment
|
|
28
|
+
are needed. This trades away the mouse-only increment, arrow keys on a focused number input still
|
|
29
29
|
work, and [data-fdy-number] gives the affordance back on the kit's terms. */
|
|
30
30
|
.fdy-input[type="number"]{appearance:textfield;-webkit-appearance:textfield;}
|
|
31
31
|
.fdy-input[type="number"]::-webkit-outer-spin-button,.fdy-input[type="number"]::-webkit-inner-spin-button{appearance:none;-webkit-appearance:none;margin:0;}
|
|
32
32
|
.fdy-input--error,.fdy-input[aria-invalid="true"],.fdy-textarea[aria-invalid="true"]{border-color:var(--color-danger);}
|
|
33
33
|
.fdy-input--error:focus,.fdy-input[aria-invalid="true"]:focus,.fdy-textarea[aria-invalid="true"]:focus{border-color:var(--color-danger);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-danger) 26%,transparent);}
|
|
34
34
|
/* Required marker. The control already carries `required`, so the asterisk is decoration: the
|
|
35
|
-
`/ ""` is CSS alt text
|
|
35
|
+
`/ ""` is CSS alt text, the glyph is painted but exposes NOTHING to the accessibility tree, so a
|
|
36
36
|
screen reader does not read "star" after every label. That is stricter than a <span> an app has to
|
|
37
37
|
remember to mark aria-hidden, and it cannot be forgotten. */
|
|
38
38
|
.fdy-label--required::after{content:"*" / "";margin-left:var(--space-1);color:var(--color-danger);}
|
package/src/components/kbd.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Keyboard key */
|
|
2
2
|
.fdy-kbd{display:inline-flex;align-items:center;justify-content:center;min-width:1.5rem;height:1.5rem;padding:0 var(--space-2);font-family:var(--font-mono);font-size:var(--text-xs);color:var(--color-text);background:var(--color-surface-2);border:var(--bw) solid var(--color-border-strong);border-bottom-width:2px;border-radius:var(--radius-sm);}
|
package/src/components/list.css
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. List: the FLAT row container.
|
|
2
2
|
*
|
|
3
3
|
* USAGE.md §3 puts list rows among the surfaces that should be flat, but until now the only
|
|
4
|
-
* container the kit shipped was .fdy-card
|
|
4
|
+
* container the kit shipped was .fdy-card, which carries --shadow-lift (a real 34px lift). A
|
|
5
5
|
* responsive table that becomes a list below `md` therefore had to choose between a stack of
|
|
6
6
|
* shadowed cards (against the doctrine) or a hand-built box, which needs a colour and so escapes
|
|
7
7
|
* the token system. This is that missing container: one flat surface, hairline dividers, no shadow.
|
|
8
8
|
*
|
|
9
9
|
* Not to be confused with .fdy-list-reset (base.css), which only strips UA bullets/indent.
|
|
10
10
|
* Works on <ul>/<ol> (list-style is reset here) or on plain <div>s. */
|
|
11
|
-
/* position:relative
|
|
11
|
+
/* position:relative, containing block for out-of-flow row content (see base.css). */
|
|
12
12
|
.fdy-list{position:relative;list-style:none;margin:0;padding:0;background:var(--color-surface);border:var(--bw) solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;}
|
|
13
13
|
/* A row that hosts a stretched target has to be the anchor itself (#008 §1). Without this the
|
|
14
|
-
overlay resolves against .fdy-list
|
|
14
|
+
overlay resolves against .fdy-list, which IS positioned, so EVERY row's target covers the whole
|
|
15
15
|
list and the last one in the DOM wins: measured, a click on row one opens row two, under row one's
|
|
16
16
|
name, which looks exactly like working software. Opt-in via :has(), so a row that doesn't use the
|
|
17
17
|
pattern is untouched and nothing an app absolutely positions inside a plain row starts resolving
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
.fdy-list__row{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-4) var(--space-5);min-width:0;}
|
|
21
21
|
/* Divider between rows. Two shapes are supported and both are load-bearing: rows as direct children
|
|
22
22
|
* of the list, and rows wrapped in <li> (the semantic shape, where the ADJACENT siblings are the
|
|
23
|
-
* <li>s
|
|
23
|
+
* <li>s, a bare `.fdy-list__row + .fdy-list__row` silently matches nothing there). */
|
|
24
24
|
.fdy-list__row + .fdy-list__row,
|
|
25
25
|
.fdy-list > * + * > .fdy-list__row{border-top:var(--bw) solid var(--color-border-muted);}
|
|
26
26
|
/* A row that is itself the control: render it as <button>/<a> and the UA box is reset without
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
.fdy-list__row--button:hover,.fdy-list__row--interactive:hover{background:var(--color-surface-2);}
|
|
30
30
|
.fdy-list__row--button:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--color-primary);}
|
|
31
31
|
.fdy-list__row--interactive{cursor:pointer;}
|
|
32
|
-
/* Disabled row. The reset above adopts the UA button box, but not its disabled state
|
|
32
|
+
/* Disabled row. The reset above adopts the UA button box, but not its disabled state, so a row
|
|
33
33
|
disabled mid-flight (a redirect in progress, say) kept lighting up and kept a pointer cursor:
|
|
34
34
|
a control answering the pointer while refusing input. Same contract as every other disabled
|
|
35
35
|
control in the kit (dim + not-allowed), hover withdrawn, both the native and the aria- form. */
|
package/src/components/menu.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Menu (action popup) + Split button. Enhanced by freeday-menu.js. */
|
|
2
2
|
.fdy-menu-wrap{position:relative;display:inline-flex;}
|
|
3
3
|
.fdy-menu{position:absolute;top:calc(100% + var(--space-1));left:0;z-index:130;min-width:11rem;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);}
|
|
4
4
|
.fdy-menu[hidden]{display:none;}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
.fdy-menu__item{display:flex;align-items:center;gap:var(--space-2);width:100%;padding:var(--space-2) var(--space-3);border:0;background:transparent;border-radius:var(--radius-sm);font-family:var(--font-body);font-size:var(--text-sm);color:var(--color-text);text-align:left;cursor:pointer;white-space:nowrap;}
|
|
7
7
|
.fdy-menu__item:hover,.fdy-menu__item.is-highlighted{background:var(--color-surface-3);}
|
|
8
8
|
/* Focus is not hover. The enhancer moves real DOM focus (freeday-menu.js `.focus()`), so this is
|
|
9
|
-
the only thing a keyboard user has to go on
|
|
9
|
+
the only thing a keyboard user has to go on, with the hover fill alone, arrowing through the
|
|
10
10
|
menu is invisible. Same ring as .fdy-nav__item, which got this right. Reported twice. */
|
|
11
11
|
.fdy-menu__item:focus-visible{outline:none;background:var(--color-surface-3);box-shadow:0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
|
|
12
12
|
.fdy-menu__item:disabled{opacity:.5;cursor:not-allowed;background:transparent;}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
.fdy-menu__sep{height:0;margin:var(--space-1) 0;border:0;border-top:var(--bw) solid var(--color-border-muted);}
|
|
16
16
|
@media (prefers-reduced-motion:no-preference){.fdy-menu:not([hidden]){animation:fdy-combo-in var(--dur-fast) var(--ease-standard);}}
|
|
17
17
|
|
|
18
|
-
/* Split button
|
|
18
|
+
/* Split button, main action + attached menu toggle (caret) */
|
|
19
19
|
.fdy-btn-split{position:relative;display:inline-flex;border-radius:var(--radius-md);transition:transform var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
|
|
20
20
|
/* One control: the two halves must stay coplanar. Neutralize each segment's own
|
|
21
21
|
lift + drop-shadow (they would misalign the seam / glow a single half); the
|
package/src/components/modal.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Modal (native <dialog>) */
|
|
2
2
|
.fdy-modal{width:min(32rem,calc(100vw - var(--space-8)));max-height:calc(100vh - var(--space-16));padding:0;border:var(--bw) solid var(--color-border);border-radius:var(--radius-xl);background:var(--color-surface);color:var(--color-text);box-shadow:var(--shadow-lift-hover);overflow:hidden;flex-direction:column;opacity:0;transform:translateY(8px) scale(.98);transition:opacity var(--dur-base) var(--ease-spring),transform var(--dur-base) var(--ease-spring),overlay var(--dur-base) allow-discrete,display var(--dur-base) allow-discrete;}
|
|
3
3
|
.fdy-modal--sm{width:min(24rem,calc(100vw - var(--space-8)));}
|
|
4
4
|
.fdy-modal--md{width:min(32rem,calc(100vw - var(--space-8)));}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Pagination */
|
|
2
2
|
.fdy-pagination__list{display:flex;align-items:center;gap:var(--space-1);list-style:none;margin:0;padding:0;}
|
|
3
3
|
.fdy-pagination__link{display:inline-flex;align-items:center;justify-content:center;min-width:2.25rem;height:2.25rem;padding:0 var(--space-2);border-radius:var(--radius-md);font-size:var(--text-sm);font-weight:var(--weight-medium);color:var(--color-text);background:transparent;border:var(--bw) solid transparent;text-decoration:none;cursor:pointer;transition:background var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard);}
|
|
4
4
|
.fdy-pagination__link:hover{background:var(--color-surface-2);border-color:var(--color-border);}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Progress */
|
|
2
2
|
.fdy-progress{height:.5rem;border-radius:var(--radius-full);background:var(--color-surface-3);overflow:hidden;}
|
|
3
3
|
.fdy-progress__bar{height:100%;border-radius:inherit;background:var(--color-primary);transition:width var(--dur-base) var(--ease-standard);}
|
|
4
4
|
.fdy-progress--indeterminate{position:relative;}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Rating (star rating). Interactive = native radio group (arrow keys, form-assoc);
|
|
2
2
|
* filled vs empty differ by fill-vs-outline (not colour alone). Enhanced by freeday-rating.js. */
|
|
3
3
|
.fdy-rating{display:inline-flex;gap:var(--space-1);}
|
|
4
4
|
.fdy-rating__star{display:inline-flex;line-height:0;cursor:pointer;}
|
|
@@ -11,6 +11,6 @@
|
|
|
11
11
|
.fdy-rating--disabled .fdy-rating__star{cursor:not-allowed;}
|
|
12
12
|
.fdy-rating--disabled .fdy-rating__star svg{opacity:.5;}
|
|
13
13
|
/* Invalid: tint the (outline) empty stars danger so a required-but-empty rating reads as an error,
|
|
14
|
-
paired with a help/error message
|
|
14
|
+
paired with a help/error message, never colour alone. */
|
|
15
15
|
.fdy-rating--error .fdy-rating__star svg{stroke:var(--color-danger);}
|
|
16
16
|
.fdy-rating--sm .fdy-rating__star svg{width:1.1rem;height:1.1rem;}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Selection controls */
|
|
2
2
|
/* `flex:none` on every box, track and standalone checkbox is load-bearing, not tidiness.
|
|
3
3
|
`.fdy-check` is an inline-flex row of [box, label], and a flex item with a WIDTH is still
|
|
4
|
-
shrinkable
|
|
4
|
+
shrinkable, so as soon as the label wrapped to a second line the box gave up horizontal space
|
|
5
5
|
to it and rendered an 18px-tall rectangle 13px wide. Squashed, off-square, and different from
|
|
6
6
|
every sibling whose label happened to be short: a form of five checkboxes showed three sizes.
|
|
7
|
-
Nothing in the declarations looked wrong, because the width was never overridden
|
|
7
|
+
Nothing in the declarations looked wrong, because the width was never overridden, it was
|
|
8
8
|
negotiated away. */
|
|
9
9
|
.fdy-check,.fdy-radio,.fdy-switch{display:inline-flex;align-items:center;gap:var(--space-2);font-size:var(--text-sm);color:var(--color-text);cursor:pointer;}
|
|
10
10
|
.fdy-check input,.fdy-radio input{appearance:none;-webkit-appearance:none;flex:none;width:var(--control-box);height:var(--control-box);margin:0;border:1.5px solid var(--color-control-border);background:var(--color-surface);box-shadow:inset 0 1px 2px var(--color-shadow-inset);cursor:pointer;display:grid;place-content:center;transition:background var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard);}
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
.fdy-switch input:checked{background:var(--color-primary);}
|
|
23
23
|
.fdy-switch input:checked::before{transform:translateX(calc(var(--control-switch-w) - var(--control-switch-h)));}
|
|
24
24
|
|
|
25
|
-
/* Standalone checkbox (e.g. table row/select-all)
|
|
25
|
+
/* Standalone checkbox (e.g. table row/select-all), supports :indeterminate */
|
|
26
26
|
.fdy-checkbox{appearance:none;-webkit-appearance:none;flex:none;width:var(--control-box);height:var(--control-box);margin:0;border:1.5px solid var(--color-control-border);border-radius:var(--radius-sm);background:var(--color-surface);box-shadow:inset 0 1px 2px var(--color-shadow-inset);cursor:pointer;display:grid;place-content:center;transition:background var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard);}
|
|
27
27
|
.fdy-checkbox::before{content:"";width:.62rem;height:.62rem;transform:scale(0);transition:transform var(--dur-fast) var(--ease-spring);box-shadow:inset 1rem 1rem var(--color-on-primary);clip-path:polygon(14% 44%,0 65%,50% 100%,100% 16%,80% 0,43% 62%);}
|
|
28
28
|
.fdy-checkbox:checked,.fdy-checkbox:indeterminate{background:var(--color-primary);border-color:var(--color-primary);}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Skeleton */
|
|
2
2
|
.fdy-skeleton{display:block;border-radius:var(--radius-sm);background:var(--color-surface-3);background-image:linear-gradient(90deg,transparent,color-mix(in srgb,var(--color-surface) 60%,transparent),transparent);background-size:200% 100%;background-repeat:no-repeat;animation:fdy-skeleton 1.4s ease-in-out infinite;}
|
|
3
3
|
.fdy-skeleton--text{height:.8rem;margin:.3rem 0;}
|
|
4
4
|
.fdy-skeleton--title{height:1.2rem;width:40%;}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Slider (native <input type="range"> themed via accent-color: keeps native
|
|
2
2
|
* keyboard + ARIA, filled track and thumb come free). Optional value output + scale.
|
|
3
3
|
* freeday-slider.js just mirrors the value into an <output for="…">. */
|
|
4
4
|
.fdy-slider{width:100%;max-width:22rem;height:1.5rem;accent-color:var(--color-primary);cursor:pointer;}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Spinner */
|
|
2
2
|
.fdy-spinner{display:inline-block;width:1.5rem;height:1.5rem;border:2.5px solid var(--color-border-strong);border-top-color:var(--color-primary);border-radius:var(--radius-full);animation:fdy-spin .7s linear infinite;}
|
|
3
3
|
.fdy-spinner--sm{width:1rem;height:1rem;border-width:2px;}
|
|
4
4
|
.fdy-spinner--lg{width:2.5rem;height:2.5rem;border-width:3px;}
|