@cahyo-dimas/freeday 1.17.0 → 1.20.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 +202 -0
- package/COMPONENTS.md +748 -0
- package/README.id.md +20 -2
- package/README.md +32 -8
- package/USAGE.md +127 -0
- package/adapters/blazor/FdyTable.razor.cs +66 -7
- package/adapters/blazor/TableTypes.cs +6 -0
- package/adapters/react/components/FdyTable.tsx +43 -8
- package/adapters/vue/components/FdyDrawer.vue +5 -2
- package/adapters/vue/components/FdyModal.vue +5 -2
- package/adapters/vue/components/FdyTable.vue +42 -6
- package/dist/freeday.bundle.css +120 -9
- package/dist/freeday.css +103 -8
- package/dist/freeday.tokens.css +17 -1
- package/docs/agent-onboarding.md +151 -0
- package/docs/getting-started.md +454 -0
- package/docs/integrations.md +321 -0
- package/docs/reference-screen.html +461 -0
- package/package.json +11 -3
- package/src/components/avatar.css +8 -8
- package/src/components/card.css +3 -0
- package/src/components/chip.css +12 -0
- package/src/components/composition.css +49 -0
- package/src/components/list.css +29 -0
- package/tokens/breakpoints.d.ts +3 -0
- package/tokens/breakpoints.mjs +8 -1
- package/tokens/tokens.json +14 -2
- package/src/components/.gitkeep +0 -0
- package/src/freeday-autocomplete.js +0 -135
- package/src/freeday-breakpoint.js +0 -51
- package/src/freeday-carousel.js +0 -111
- package/src/freeday-cascade.js +0 -256
- package/src/freeday-cfl.js +0 -213
- package/src/freeday-chart.js +0 -429
- package/src/freeday-chip.js +0 -83
- package/src/freeday-datepicker.js +0 -321
- package/src/freeday-datetime.js +0 -83
- package/src/freeday-drawer.js +0 -43
- package/src/freeday-form.js +0 -181
- package/src/freeday-mask.js +0 -114
- package/src/freeday-menu.js +0 -93
- package/src/freeday-popover.js +0 -69
- package/src/freeday-rating.js +0 -50
- package/src/freeday-select.js +0 -218
- package/src/freeday-slider.js +0 -34
- package/src/freeday-stepper.js +0 -90
- package/src/freeday-table.js +0 -475
- package/src/freeday-tabs.js +0 -68
- package/src/freeday-timepicker.js +0 -180
- package/src/freeday-toast.js +0 -105
- package/src/freeday-tree.js +0 -94
- package/src/freeday-upload.js +0 -206
package/dist/freeday.bundle.css
CHANGED
|
@@ -166,6 +166,14 @@
|
|
|
166
166
|
--chart-8: #e34948;
|
|
167
167
|
--chart-grid: var(--slate-200);
|
|
168
168
|
--chart-tick: var(--slate-400);
|
|
169
|
+
--tone-1: var(--chart-1);
|
|
170
|
+
--tone-2: var(--chart-2);
|
|
171
|
+
--tone-3: var(--chart-3);
|
|
172
|
+
--tone-4: var(--chart-4);
|
|
173
|
+
--tone-5: var(--chart-5);
|
|
174
|
+
--tone-6: var(--chart-6);
|
|
175
|
+
--tone-7: var(--chart-7);
|
|
176
|
+
--tone-8: var(--chart-8);
|
|
169
177
|
--focus-ring: var(--azure-600);
|
|
170
178
|
--focus-ring-width: 2px;
|
|
171
179
|
}
|
|
@@ -339,7 +347,15 @@
|
|
|
339
347
|
--chart-tick: var(--slate-400);
|
|
340
348
|
--focus-ring: var(--azure-600);
|
|
341
349
|
}
|
|
342
|
-
:root
|
|
350
|
+
/* Density is deliberately NOT scoped to :root. These are custom properties, so they inherit —
|
|
351
|
+
* putting the attribute on any ancestor (a route wrapper, one section) densifies just that
|
|
352
|
+
* subtree, which is how density is actually decided: per screen, not per app. The root still
|
|
353
|
+
* matches, so setting data-density on the html element keeps working exactly as before. */
|
|
354
|
+
[data-density="compact"] {
|
|
355
|
+
--space-3: 0.625rem;
|
|
356
|
+
--space-4: 0.75rem;
|
|
357
|
+
--space-5: 1rem;
|
|
358
|
+
--space-6: 1.25rem;
|
|
343
359
|
--control-h: 2rem;
|
|
344
360
|
}
|
|
345
361
|
|
|
@@ -534,14 +550,14 @@ a { color: var(--color-primary); }
|
|
|
534
550
|
* test/contrast.test.mjs). Use to disambiguate same-initial avatars (e.g. many "PT …" names):
|
|
535
551
|
* hash a stable 1..8 index off the FULL name in your app, then apply .fdy-avatar--tone-<n>. */
|
|
536
552
|
.fdy-avatar--tone-1,.fdy-avatar--tone-2,.fdy-avatar--tone-3,.fdy-avatar--tone-4,.fdy-avatar--tone-5,.fdy-avatar--tone-6,.fdy-avatar--tone-7,.fdy-avatar--tone-8{background:color-mix(in srgb,var(--_fdy-avatar-tone) 18%,var(--color-surface));color:color-mix(in srgb,var(--_fdy-avatar-tone) 50%,var(--color-text));}
|
|
537
|
-
.fdy-avatar--tone-1{--_fdy-avatar-tone:var(--
|
|
538
|
-
.fdy-avatar--tone-2{--_fdy-avatar-tone:var(--
|
|
539
|
-
.fdy-avatar--tone-3{--_fdy-avatar-tone:var(--
|
|
540
|
-
.fdy-avatar--tone-4{--_fdy-avatar-tone:var(--
|
|
541
|
-
.fdy-avatar--tone-5{--_fdy-avatar-tone:var(--
|
|
542
|
-
.fdy-avatar--tone-6{--_fdy-avatar-tone:var(--
|
|
543
|
-
.fdy-avatar--tone-7{--_fdy-avatar-tone:var(--
|
|
544
|
-
.fdy-avatar--tone-8{--_fdy-avatar-tone:var(--
|
|
553
|
+
.fdy-avatar--tone-1{--_fdy-avatar-tone:var(--tone-1);}
|
|
554
|
+
.fdy-avatar--tone-2{--_fdy-avatar-tone:var(--tone-2);}
|
|
555
|
+
.fdy-avatar--tone-3{--_fdy-avatar-tone:var(--tone-3);}
|
|
556
|
+
.fdy-avatar--tone-4{--_fdy-avatar-tone:var(--tone-4);}
|
|
557
|
+
.fdy-avatar--tone-5{--_fdy-avatar-tone:var(--tone-5);}
|
|
558
|
+
.fdy-avatar--tone-6{--_fdy-avatar-tone:var(--tone-6);}
|
|
559
|
+
.fdy-avatar--tone-7{--_fdy-avatar-tone:var(--tone-7);}
|
|
560
|
+
.fdy-avatar--tone-8{--_fdy-avatar-tone:var(--tone-8);}
|
|
545
561
|
|
|
546
562
|
/* Freeday — Badge */
|
|
547
563
|
.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);}
|
|
@@ -661,6 +677,9 @@ a { color: var(--color-primary); }
|
|
|
661
677
|
.fdy-card--elevated{box-shadow:var(--shadow-lift-hover);}
|
|
662
678
|
.fdy-card--interactive{cursor:pointer;}
|
|
663
679
|
.fdy-card--interactive:hover{box-shadow:var(--shadow-lift-hover);transform:translateY(-3px);}
|
|
680
|
+
/* The global reduced-motion reset kills the ANIMATION; drop the -3px shift too so the card
|
|
681
|
+
* doesn't jump. The shadow still communicates the affordance. */
|
|
682
|
+
@media (prefers-reduced-motion:reduce){.fdy-card--interactive:hover{transform:none;}}
|
|
664
683
|
/* When the whole card IS the control — a real <button> so the row is keyboard-reachable and
|
|
665
684
|
* announced — reset the UA button box. Note: it does NOT reset background/border, because
|
|
666
685
|
* .fdy-card already sets its own surface + border (resetting them here would strip the card). */
|
|
@@ -816,6 +835,18 @@ a { color: var(--color-primary); }
|
|
|
816
835
|
/* Freeday — Chip (tag / removable filter) */
|
|
817
836
|
.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);}
|
|
818
837
|
.fdy-chip--primary{color:var(--color-primary-strong);background:var(--color-primary-soft);border-color:color-mix(in srgb,var(--color-primary) 22%,transparent);}
|
|
838
|
+
/* Categorical tone chips — non-semantic category tags / legend swatches, from the general
|
|
839
|
+
* --tone-1..8 palette. Same validated AA formula as .fdy-avatar--tone-* (text >=4.5:1 both themes,
|
|
840
|
+
* gated by test/contrast.test.mjs). */
|
|
841
|
+
.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));}
|
|
842
|
+
.fdy-chip--tone-1{--_fdy-chip-tone:var(--tone-1);}
|
|
843
|
+
.fdy-chip--tone-2{--_fdy-chip-tone:var(--tone-2);}
|
|
844
|
+
.fdy-chip--tone-3{--_fdy-chip-tone:var(--tone-3);}
|
|
845
|
+
.fdy-chip--tone-4{--_fdy-chip-tone:var(--tone-4);}
|
|
846
|
+
.fdy-chip--tone-5{--_fdy-chip-tone:var(--tone-5);}
|
|
847
|
+
.fdy-chip--tone-6{--_fdy-chip-tone:var(--tone-6);}
|
|
848
|
+
.fdy-chip--tone-7{--_fdy-chip-tone:var(--tone-7);}
|
|
849
|
+
.fdy-chip--tone-8{--_fdy-chip-tone:var(--tone-8);}
|
|
819
850
|
.fdy-chip__remove{display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;margin-right:calc(var(--space-1)*-1);border:0;padding:0;border-radius:var(--radius-full);background:transparent;color:currentColor;opacity:.6;cursor:pointer;font-size:var(--text-sm);line-height:1;}
|
|
820
851
|
.fdy-chip__remove:hover{opacity:1;background:color-mix(in srgb,currentColor 16%,transparent);}
|
|
821
852
|
.fdy-chip__remove:focus-visible{outline:none;opacity:1;box-shadow:0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
|
|
@@ -865,6 +896,56 @@ a { color: var(--color-primary); }
|
|
|
865
896
|
@media (prefers-reduced-motion:no-preference){.fdy-combo__listbox:not([hidden]){animation:fdy-combo-in var(--dur-fast) var(--ease-standard);}}
|
|
866
897
|
@keyframes fdy-combo-in{from{opacity:0;transform:translateY(-4px);}to{opacity:1;transform:none;}}
|
|
867
898
|
|
|
899
|
+
/* Freeday — Page composition & type roles.
|
|
900
|
+
*
|
|
901
|
+
* The kit stops at the component; these primitives encode *how a page is assembled* so screens
|
|
902
|
+
* built by different people cohere. They carry the usage doctrine (see USAGE.md) in markup: a
|
|
903
|
+
* named type role gets used, a doc gets skimmed. Everything here is token-driven layout — no new
|
|
904
|
+
* colours, no new shadows. Pair with .fdy-app (the shell) and the components inside.
|
|
905
|
+
*/
|
|
906
|
+
|
|
907
|
+
/* ---- Type roles (three title levels + supporting text) -------------------------------------
|
|
908
|
+
* One role per level of hierarchy. Reach for these instead of re-using .fdy-card__title for a
|
|
909
|
+
* page title (the mistake that collapses three levels into one grey mush). */
|
|
910
|
+
.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-subtle);}
|
|
911
|
+
.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;}
|
|
912
|
+
.fdy-title-section{font-family:var(--font-display);font-size:var(--text-xl);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-tight);line-height:var(--leading-snug);color:var(--color-text);margin:0;text-wrap:balance;}
|
|
913
|
+
.fdy-title-card{font-family:var(--font-display);font-size:var(--text-lg);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-tight);color:var(--color-text);margin:0;}
|
|
914
|
+
.fdy-text-muted{color:var(--color-text-muted);}
|
|
915
|
+
.fdy-text-subtle{color:var(--color-text-subtle);}
|
|
916
|
+
.fdy-text-caption{font-size:var(--text-sm);color:var(--color-text-muted);margin:0;}
|
|
917
|
+
|
|
918
|
+
/* ---- Page: the vertical stack of sections, with the section rhythm between them ------------- */
|
|
919
|
+
.fdy-page{display:flex;flex-direction:column;gap:var(--space-8);}
|
|
920
|
+
|
|
921
|
+
/* ---- Page header: eyebrow + title + description on the left, primary action(s) on the right -- */
|
|
922
|
+
.fdy-page__header{display:flex;align-items:flex-end;justify-content:space-between;gap:var(--space-4);flex-wrap:wrap;}
|
|
923
|
+
.fdy-page__heading{display:flex;flex-direction:column;gap:var(--space-2);min-width:0;}
|
|
924
|
+
.fdy-page__desc{font-size:var(--text-base);color:var(--color-text-muted);margin:0;max-width:62ch;line-height:var(--leading-normal);}
|
|
925
|
+
.fdy-page__actions{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;}
|
|
926
|
+
|
|
927
|
+
/* ---- Page section: a heading (+ optional toolbar) and its body, with the intra-group rhythm --- */
|
|
928
|
+
.fdy-page-section{display:flex;flex-direction:column;gap:var(--space-4);}
|
|
929
|
+
.fdy-page-section__head{display:flex;align-items:baseline;justify-content:space-between;gap:var(--space-3);flex-wrap:wrap;}
|
|
930
|
+
|
|
931
|
+
/* ---- Toolbar: a horizontal control row (search + actions), wraps cleanly ---------------------- */
|
|
932
|
+
.fdy-toolbar{display:flex;align-items:center;gap:var(--space-3);flex-wrap:wrap;}
|
|
933
|
+
.fdy-toolbar__spacer{flex:1;}
|
|
934
|
+
.fdy-toolbar__search{max-width:18rem;flex:1 1 12rem;}
|
|
935
|
+
|
|
936
|
+
/* ---- Stat tile: a KPI metric that is deliberately NOT a card, so a KPI strip doesn't become an
|
|
937
|
+
* identical-white-card grid. Flat by default; wrap the .fdy-stats grid in one card if you want a
|
|
938
|
+
* single boxed strip, or use .fdy-stats--boxed for shared surface + dividers. */
|
|
939
|
+
.fdy-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));gap:var(--space-5);}
|
|
940
|
+
.fdy-stat{display:flex;flex-direction:column;gap:var(--space-1);min-width:0;}
|
|
941
|
+
.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-subtle);}
|
|
942
|
+
.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;}
|
|
943
|
+
.fdy-stat__value small{font-size:var(--text-lg);font-weight:var(--weight-semibold);color:var(--color-text-muted);}
|
|
944
|
+
.fdy-stat__meta{font-size:var(--text-sm);color:var(--color-text-muted);display:flex;align-items:center;gap:var(--space-2);}
|
|
945
|
+
.fdy-stats--boxed{background:var(--color-surface);border:var(--bw) solid var(--color-border);border-radius:var(--radius-lg);box-shadow:var(--shadow-1);gap:0;}
|
|
946
|
+
.fdy-stats--boxed .fdy-stat{padding:var(--space-5);border-right:var(--bw) solid var(--color-border-muted);}
|
|
947
|
+
.fdy-stats--boxed .fdy-stat:last-child{border-right:0;}
|
|
948
|
+
|
|
868
949
|
/* Freeday — Date picker (input-styled trigger + calendar popover).
|
|
869
950
|
* Built by freeday-datepicker.js from an empty [data-fdy-datepicker] wrapper. */
|
|
870
951
|
.fdy-datepicker{position:relative;display:inline-block;width:100%;max-width:14rem;}
|
|
@@ -1107,6 +1188,36 @@ fieldset.fdy-field>legend{padding:0;float:none;}
|
|
|
1107
1188
|
/* Freeday — Keyboard key */
|
|
1108
1189
|
.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);}
|
|
1109
1190
|
|
|
1191
|
+
/* Freeday — List: the FLAT row container.
|
|
1192
|
+
*
|
|
1193
|
+
* USAGE.md §3 puts list rows among the surfaces that should be flat, but until now the only
|
|
1194
|
+
* container the kit shipped was .fdy-card — which carries --shadow-lift (a real 34px lift). A
|
|
1195
|
+
* responsive table that becomes a list below `md` therefore had to choose between a stack of
|
|
1196
|
+
* shadowed cards (against the doctrine) or a hand-built box, which needs a colour and so escapes
|
|
1197
|
+
* the token system. This is that missing container: one flat surface, hairline dividers, no shadow.
|
|
1198
|
+
*
|
|
1199
|
+
* Not to be confused with .fdy-list-reset (base.css), which only strips UA bullets/indent.
|
|
1200
|
+
* Works on <ul>/<ol> (list-style is reset here) or on plain <div>s. */
|
|
1201
|
+
.fdy-list{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;}
|
|
1202
|
+
.fdy-list__row{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-4) var(--space-5);min-width:0;}
|
|
1203
|
+
/* Divider between rows. Two shapes are supported and both are load-bearing: rows as direct children
|
|
1204
|
+
* of the list, and rows wrapped in <li> (the semantic shape, where the ADJACENT siblings are the
|
|
1205
|
+
* <li>s — a bare `.fdy-list__row + .fdy-list__row` silently matches nothing there). */
|
|
1206
|
+
.fdy-list__row + .fdy-list__row,
|
|
1207
|
+
.fdy-list > * + * > .fdy-list__row{border-top:var(--bw) solid var(--color-border-muted);}
|
|
1208
|
+
/* A row that is itself the control: render it as <button>/<a> and the UA box is reset without
|
|
1209
|
+
* touching the list surface (same contract as .fdy-card--button). */
|
|
1210
|
+
.fdy-list__row--button{width:100%;text-align:inherit;color:inherit;font:inherit;background:none;border:0;appearance:none;-webkit-appearance:none;cursor:pointer;}
|
|
1211
|
+
.fdy-list__row--button:hover,.fdy-list__row--interactive:hover{background:var(--color-surface-2);}
|
|
1212
|
+
.fdy-list__row--button:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--color-primary);}
|
|
1213
|
+
.fdy-list__row--interactive{cursor:pointer;}
|
|
1214
|
+
/* Row internals: a title/meta stack that truncates, and a trailing slot pinned right. */
|
|
1215
|
+
.fdy-list__main{display:flex;flex-direction:column;gap:var(--space-1);min-width:0;flex:1;}
|
|
1216
|
+
.fdy-list__title{font-weight:var(--weight-medium);color:var(--color-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
1217
|
+
.fdy-list__meta{font-size:var(--text-sm);color:var(--color-text-muted);display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;}
|
|
1218
|
+
.fdy-list__aside{flex:none;display:flex;align-items:center;gap:var(--space-2);}
|
|
1219
|
+
/* Density: rows tighten with the rest of the kit (--space-* mid-range steps under compact). */
|
|
1220
|
+
|
|
1110
1221
|
/* Freeday — Menu (action popup) + Split button. Enhanced by freeday-menu.js. */
|
|
1111
1222
|
.fdy-menu-wrap{position:relative;display:inline-flex;}
|
|
1112
1223
|
.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);}
|
package/dist/freeday.css
CHANGED
|
@@ -189,14 +189,14 @@ a { color: var(--color-primary); }
|
|
|
189
189
|
* test/contrast.test.mjs). Use to disambiguate same-initial avatars (e.g. many "PT …" names):
|
|
190
190
|
* hash a stable 1..8 index off the FULL name in your app, then apply .fdy-avatar--tone-<n>. */
|
|
191
191
|
.fdy-avatar--tone-1,.fdy-avatar--tone-2,.fdy-avatar--tone-3,.fdy-avatar--tone-4,.fdy-avatar--tone-5,.fdy-avatar--tone-6,.fdy-avatar--tone-7,.fdy-avatar--tone-8{background:color-mix(in srgb,var(--_fdy-avatar-tone) 18%,var(--color-surface));color:color-mix(in srgb,var(--_fdy-avatar-tone) 50%,var(--color-text));}
|
|
192
|
-
.fdy-avatar--tone-1{--_fdy-avatar-tone:var(--
|
|
193
|
-
.fdy-avatar--tone-2{--_fdy-avatar-tone:var(--
|
|
194
|
-
.fdy-avatar--tone-3{--_fdy-avatar-tone:var(--
|
|
195
|
-
.fdy-avatar--tone-4{--_fdy-avatar-tone:var(--
|
|
196
|
-
.fdy-avatar--tone-5{--_fdy-avatar-tone:var(--
|
|
197
|
-
.fdy-avatar--tone-6{--_fdy-avatar-tone:var(--
|
|
198
|
-
.fdy-avatar--tone-7{--_fdy-avatar-tone:var(--
|
|
199
|
-
.fdy-avatar--tone-8{--_fdy-avatar-tone:var(--
|
|
192
|
+
.fdy-avatar--tone-1{--_fdy-avatar-tone:var(--tone-1);}
|
|
193
|
+
.fdy-avatar--tone-2{--_fdy-avatar-tone:var(--tone-2);}
|
|
194
|
+
.fdy-avatar--tone-3{--_fdy-avatar-tone:var(--tone-3);}
|
|
195
|
+
.fdy-avatar--tone-4{--_fdy-avatar-tone:var(--tone-4);}
|
|
196
|
+
.fdy-avatar--tone-5{--_fdy-avatar-tone:var(--tone-5);}
|
|
197
|
+
.fdy-avatar--tone-6{--_fdy-avatar-tone:var(--tone-6);}
|
|
198
|
+
.fdy-avatar--tone-7{--_fdy-avatar-tone:var(--tone-7);}
|
|
199
|
+
.fdy-avatar--tone-8{--_fdy-avatar-tone:var(--tone-8);}
|
|
200
200
|
|
|
201
201
|
/* Freeday — Badge */
|
|
202
202
|
.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);}
|
|
@@ -316,6 +316,9 @@ a { color: var(--color-primary); }
|
|
|
316
316
|
.fdy-card--elevated{box-shadow:var(--shadow-lift-hover);}
|
|
317
317
|
.fdy-card--interactive{cursor:pointer;}
|
|
318
318
|
.fdy-card--interactive:hover{box-shadow:var(--shadow-lift-hover);transform:translateY(-3px);}
|
|
319
|
+
/* The global reduced-motion reset kills the ANIMATION; drop the -3px shift too so the card
|
|
320
|
+
* doesn't jump. The shadow still communicates the affordance. */
|
|
321
|
+
@media (prefers-reduced-motion:reduce){.fdy-card--interactive:hover{transform:none;}}
|
|
319
322
|
/* When the whole card IS the control — a real <button> so the row is keyboard-reachable and
|
|
320
323
|
* announced — reset the UA button box. Note: it does NOT reset background/border, because
|
|
321
324
|
* .fdy-card already sets its own surface + border (resetting them here would strip the card). */
|
|
@@ -471,6 +474,18 @@ a { color: var(--color-primary); }
|
|
|
471
474
|
/* Freeday — Chip (tag / removable filter) */
|
|
472
475
|
.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);}
|
|
473
476
|
.fdy-chip--primary{color:var(--color-primary-strong);background:var(--color-primary-soft);border-color:color-mix(in srgb,var(--color-primary) 22%,transparent);}
|
|
477
|
+
/* Categorical tone chips — non-semantic category tags / legend swatches, from the general
|
|
478
|
+
* --tone-1..8 palette. Same validated AA formula as .fdy-avatar--tone-* (text >=4.5:1 both themes,
|
|
479
|
+
* gated by test/contrast.test.mjs). */
|
|
480
|
+
.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));}
|
|
481
|
+
.fdy-chip--tone-1{--_fdy-chip-tone:var(--tone-1);}
|
|
482
|
+
.fdy-chip--tone-2{--_fdy-chip-tone:var(--tone-2);}
|
|
483
|
+
.fdy-chip--tone-3{--_fdy-chip-tone:var(--tone-3);}
|
|
484
|
+
.fdy-chip--tone-4{--_fdy-chip-tone:var(--tone-4);}
|
|
485
|
+
.fdy-chip--tone-5{--_fdy-chip-tone:var(--tone-5);}
|
|
486
|
+
.fdy-chip--tone-6{--_fdy-chip-tone:var(--tone-6);}
|
|
487
|
+
.fdy-chip--tone-7{--_fdy-chip-tone:var(--tone-7);}
|
|
488
|
+
.fdy-chip--tone-8{--_fdy-chip-tone:var(--tone-8);}
|
|
474
489
|
.fdy-chip__remove{display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;margin-right:calc(var(--space-1)*-1);border:0;padding:0;border-radius:var(--radius-full);background:transparent;color:currentColor;opacity:.6;cursor:pointer;font-size:var(--text-sm);line-height:1;}
|
|
475
490
|
.fdy-chip__remove:hover{opacity:1;background:color-mix(in srgb,currentColor 16%,transparent);}
|
|
476
491
|
.fdy-chip__remove:focus-visible{outline:none;opacity:1;box-shadow:0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
|
|
@@ -520,6 +535,56 @@ a { color: var(--color-primary); }
|
|
|
520
535
|
@media (prefers-reduced-motion:no-preference){.fdy-combo__listbox:not([hidden]){animation:fdy-combo-in var(--dur-fast) var(--ease-standard);}}
|
|
521
536
|
@keyframes fdy-combo-in{from{opacity:0;transform:translateY(-4px);}to{opacity:1;transform:none;}}
|
|
522
537
|
|
|
538
|
+
/* Freeday — Page composition & type roles.
|
|
539
|
+
*
|
|
540
|
+
* The kit stops at the component; these primitives encode *how a page is assembled* so screens
|
|
541
|
+
* built by different people cohere. They carry the usage doctrine (see USAGE.md) in markup: a
|
|
542
|
+
* named type role gets used, a doc gets skimmed. Everything here is token-driven layout — no new
|
|
543
|
+
* colours, no new shadows. Pair with .fdy-app (the shell) and the components inside.
|
|
544
|
+
*/
|
|
545
|
+
|
|
546
|
+
/* ---- Type roles (three title levels + supporting text) -------------------------------------
|
|
547
|
+
* One role per level of hierarchy. Reach for these instead of re-using .fdy-card__title for a
|
|
548
|
+
* page title (the mistake that collapses three levels into one grey mush). */
|
|
549
|
+
.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-subtle);}
|
|
550
|
+
.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;}
|
|
551
|
+
.fdy-title-section{font-family:var(--font-display);font-size:var(--text-xl);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-tight);line-height:var(--leading-snug);color:var(--color-text);margin:0;text-wrap:balance;}
|
|
552
|
+
.fdy-title-card{font-family:var(--font-display);font-size:var(--text-lg);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-tight);color:var(--color-text);margin:0;}
|
|
553
|
+
.fdy-text-muted{color:var(--color-text-muted);}
|
|
554
|
+
.fdy-text-subtle{color:var(--color-text-subtle);}
|
|
555
|
+
.fdy-text-caption{font-size:var(--text-sm);color:var(--color-text-muted);margin:0;}
|
|
556
|
+
|
|
557
|
+
/* ---- Page: the vertical stack of sections, with the section rhythm between them ------------- */
|
|
558
|
+
.fdy-page{display:flex;flex-direction:column;gap:var(--space-8);}
|
|
559
|
+
|
|
560
|
+
/* ---- Page header: eyebrow + title + description on the left, primary action(s) on the right -- */
|
|
561
|
+
.fdy-page__header{display:flex;align-items:flex-end;justify-content:space-between;gap:var(--space-4);flex-wrap:wrap;}
|
|
562
|
+
.fdy-page__heading{display:flex;flex-direction:column;gap:var(--space-2);min-width:0;}
|
|
563
|
+
.fdy-page__desc{font-size:var(--text-base);color:var(--color-text-muted);margin:0;max-width:62ch;line-height:var(--leading-normal);}
|
|
564
|
+
.fdy-page__actions{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;}
|
|
565
|
+
|
|
566
|
+
/* ---- Page section: a heading (+ optional toolbar) and its body, with the intra-group rhythm --- */
|
|
567
|
+
.fdy-page-section{display:flex;flex-direction:column;gap:var(--space-4);}
|
|
568
|
+
.fdy-page-section__head{display:flex;align-items:baseline;justify-content:space-between;gap:var(--space-3);flex-wrap:wrap;}
|
|
569
|
+
|
|
570
|
+
/* ---- Toolbar: a horizontal control row (search + actions), wraps cleanly ---------------------- */
|
|
571
|
+
.fdy-toolbar{display:flex;align-items:center;gap:var(--space-3);flex-wrap:wrap;}
|
|
572
|
+
.fdy-toolbar__spacer{flex:1;}
|
|
573
|
+
.fdy-toolbar__search{max-width:18rem;flex:1 1 12rem;}
|
|
574
|
+
|
|
575
|
+
/* ---- Stat tile: a KPI metric that is deliberately NOT a card, so a KPI strip doesn't become an
|
|
576
|
+
* identical-white-card grid. Flat by default; wrap the .fdy-stats grid in one card if you want a
|
|
577
|
+
* single boxed strip, or use .fdy-stats--boxed for shared surface + dividers. */
|
|
578
|
+
.fdy-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));gap:var(--space-5);}
|
|
579
|
+
.fdy-stat{display:flex;flex-direction:column;gap:var(--space-1);min-width:0;}
|
|
580
|
+
.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-subtle);}
|
|
581
|
+
.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;}
|
|
582
|
+
.fdy-stat__value small{font-size:var(--text-lg);font-weight:var(--weight-semibold);color:var(--color-text-muted);}
|
|
583
|
+
.fdy-stat__meta{font-size:var(--text-sm);color:var(--color-text-muted);display:flex;align-items:center;gap:var(--space-2);}
|
|
584
|
+
.fdy-stats--boxed{background:var(--color-surface);border:var(--bw) solid var(--color-border);border-radius:var(--radius-lg);box-shadow:var(--shadow-1);gap:0;}
|
|
585
|
+
.fdy-stats--boxed .fdy-stat{padding:var(--space-5);border-right:var(--bw) solid var(--color-border-muted);}
|
|
586
|
+
.fdy-stats--boxed .fdy-stat:last-child{border-right:0;}
|
|
587
|
+
|
|
523
588
|
/* Freeday — Date picker (input-styled trigger + calendar popover).
|
|
524
589
|
* Built by freeday-datepicker.js from an empty [data-fdy-datepicker] wrapper. */
|
|
525
590
|
.fdy-datepicker{position:relative;display:inline-block;width:100%;max-width:14rem;}
|
|
@@ -762,6 +827,36 @@ fieldset.fdy-field>legend{padding:0;float:none;}
|
|
|
762
827
|
/* Freeday — Keyboard key */
|
|
763
828
|
.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);}
|
|
764
829
|
|
|
830
|
+
/* Freeday — List: the FLAT row container.
|
|
831
|
+
*
|
|
832
|
+
* USAGE.md §3 puts list rows among the surfaces that should be flat, but until now the only
|
|
833
|
+
* container the kit shipped was .fdy-card — which carries --shadow-lift (a real 34px lift). A
|
|
834
|
+
* responsive table that becomes a list below `md` therefore had to choose between a stack of
|
|
835
|
+
* shadowed cards (against the doctrine) or a hand-built box, which needs a colour and so escapes
|
|
836
|
+
* the token system. This is that missing container: one flat surface, hairline dividers, no shadow.
|
|
837
|
+
*
|
|
838
|
+
* Not to be confused with .fdy-list-reset (base.css), which only strips UA bullets/indent.
|
|
839
|
+
* Works on <ul>/<ol> (list-style is reset here) or on plain <div>s. */
|
|
840
|
+
.fdy-list{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;}
|
|
841
|
+
.fdy-list__row{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-4) var(--space-5);min-width:0;}
|
|
842
|
+
/* Divider between rows. Two shapes are supported and both are load-bearing: rows as direct children
|
|
843
|
+
* of the list, and rows wrapped in <li> (the semantic shape, where the ADJACENT siblings are the
|
|
844
|
+
* <li>s — a bare `.fdy-list__row + .fdy-list__row` silently matches nothing there). */
|
|
845
|
+
.fdy-list__row + .fdy-list__row,
|
|
846
|
+
.fdy-list > * + * > .fdy-list__row{border-top:var(--bw) solid var(--color-border-muted);}
|
|
847
|
+
/* A row that is itself the control: render it as <button>/<a> and the UA box is reset without
|
|
848
|
+
* touching the list surface (same contract as .fdy-card--button). */
|
|
849
|
+
.fdy-list__row--button{width:100%;text-align:inherit;color:inherit;font:inherit;background:none;border:0;appearance:none;-webkit-appearance:none;cursor:pointer;}
|
|
850
|
+
.fdy-list__row--button:hover,.fdy-list__row--interactive:hover{background:var(--color-surface-2);}
|
|
851
|
+
.fdy-list__row--button:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--color-primary);}
|
|
852
|
+
.fdy-list__row--interactive{cursor:pointer;}
|
|
853
|
+
/* Row internals: a title/meta stack that truncates, and a trailing slot pinned right. */
|
|
854
|
+
.fdy-list__main{display:flex;flex-direction:column;gap:var(--space-1);min-width:0;flex:1;}
|
|
855
|
+
.fdy-list__title{font-weight:var(--weight-medium);color:var(--color-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
856
|
+
.fdy-list__meta{font-size:var(--text-sm);color:var(--color-text-muted);display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;}
|
|
857
|
+
.fdy-list__aside{flex:none;display:flex;align-items:center;gap:var(--space-2);}
|
|
858
|
+
/* Density: rows tighten with the rest of the kit (--space-* mid-range steps under compact). */
|
|
859
|
+
|
|
765
860
|
/* Freeday — Menu (action popup) + Split button. Enhanced by freeday-menu.js. */
|
|
766
861
|
.fdy-menu-wrap{position:relative;display:inline-flex;}
|
|
767
862
|
.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);}
|
package/dist/freeday.tokens.css
CHANGED
|
@@ -165,6 +165,14 @@
|
|
|
165
165
|
--chart-8: #e34948;
|
|
166
166
|
--chart-grid: var(--slate-200);
|
|
167
167
|
--chart-tick: var(--slate-400);
|
|
168
|
+
--tone-1: var(--chart-1);
|
|
169
|
+
--tone-2: var(--chart-2);
|
|
170
|
+
--tone-3: var(--chart-3);
|
|
171
|
+
--tone-4: var(--chart-4);
|
|
172
|
+
--tone-5: var(--chart-5);
|
|
173
|
+
--tone-6: var(--chart-6);
|
|
174
|
+
--tone-7: var(--chart-7);
|
|
175
|
+
--tone-8: var(--chart-8);
|
|
168
176
|
--focus-ring: var(--azure-600);
|
|
169
177
|
--focus-ring-width: 2px;
|
|
170
178
|
}
|
|
@@ -338,6 +346,14 @@
|
|
|
338
346
|
--chart-tick: var(--slate-400);
|
|
339
347
|
--focus-ring: var(--azure-600);
|
|
340
348
|
}
|
|
341
|
-
:root
|
|
349
|
+
/* Density is deliberately NOT scoped to :root. These are custom properties, so they inherit —
|
|
350
|
+
* putting the attribute on any ancestor (a route wrapper, one section) densifies just that
|
|
351
|
+
* subtree, which is how density is actually decided: per screen, not per app. The root still
|
|
352
|
+
* matches, so setting data-density on the html element keeps working exactly as before. */
|
|
353
|
+
[data-density="compact"] {
|
|
354
|
+
--space-3: 0.625rem;
|
|
355
|
+
--space-4: 0.75rem;
|
|
356
|
+
--space-5: 1rem;
|
|
357
|
+
--space-6: 1.25rem;
|
|
342
358
|
--control-h: 2rem;
|
|
343
359
|
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Freeday — AI agent onboarding
|
|
2
|
+
|
|
3
|
+
For **a coding agent working in a project that consumes Freeday** (Claude Code, Codex, Cursor,
|
|
4
|
+
Copilot…). No model has Freeday in its training data, so an agent that is merely told "use Freeday"
|
|
5
|
+
will invent class names or silently fall back to Bootstrap/Tailwind conventions. This file is the
|
|
6
|
+
fix: paste the block below into the consuming project's agent instruction file, once.
|
|
7
|
+
|
|
8
|
+
> Working on **the kit itself**, not a consuming app? That's [`../CLAUDE.md`](../CLAUDE.md) — this
|
|
9
|
+
> file is about *using* the published package.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. Paste this into your project's agent instructions
|
|
14
|
+
|
|
15
|
+
Into `CLAUDE.md` (Claude Code) / `AGENTS.md` (Codex, others) / `.github/copilot-instructions.md` at
|
|
16
|
+
the **root of the consuming project**:
|
|
17
|
+
|
|
18
|
+
```markdown
|
|
19
|
+
## UI: Freeday design system (@cahyo-dimas/freeday)
|
|
20
|
+
|
|
21
|
+
All UI in this project is built from Freeday. It is a **token-driven CSS kit + zero-dependency JS
|
|
22
|
+
enhancers**, not a component framework — components are plain markup with `fdy-*` classes.
|
|
23
|
+
|
|
24
|
+
**Before writing or editing any markup/CSS, read these (they ship inside the package):**
|
|
25
|
+
- `node_modules/@cahyo-dimas/freeday/COMPONENTS.md` — every class that exists, with minimal markup
|
|
26
|
+
skeletons, enhancer hooks and the a11y contract per component. **The class list is closed:
|
|
27
|
+
if a class is not in that file, it does not exist — do not invent one.**
|
|
28
|
+
- `node_modules/@cahyo-dimas/freeday/USAGE.md` — the doctrine: which token/role/shadow to use when.
|
|
29
|
+
- `node_modules/@cahyo-dimas/freeday/docs/reference-screen.html` — one complete screen, assembled
|
|
30
|
+
the intended way. Copy this structure for a new screen.
|
|
31
|
+
|
|
32
|
+
**Non-negotiables:**
|
|
33
|
+
1. No raw hex or px in app CSS. Use tokens: `var(--color-primary)`, `var(--space-4)` (4px scale),
|
|
34
|
+
`var(--radius-md)`, `var(--shadow-1)`, `var(--dur-2)`.
|
|
35
|
+
2. Components only touch semantic tokens (`--color-*`) — never the primitive ramp (`--azure-600`).
|
|
36
|
+
3. `.fdy-btn` is already the primary action (there is no `--primary`). One per screen; everything
|
|
37
|
+
else is `--ghost` or `--text`.
|
|
38
|
+
4. Three title roles only: `.fdy-title-page` (one `<h1>`) / `.fdy-title-section` / `.fdy-title-card`.
|
|
39
|
+
Never reuse a card title as a page title.
|
|
40
|
+
5. Assemble from the frame down: `.fdy-app` → `.fdy-page` → `.fdy-page-section` → components.
|
|
41
|
+
6. Form errors: `aria-invalid="true"` + `aria-describedby` → a `.fdy-help.fdy-help--error`.
|
|
42
|
+
Icon-only buttons need `aria-label`. Status is never colour-only.
|
|
43
|
+
7. Interactive components need their enhancer script loaded (see the table in COMPONENTS.md);
|
|
44
|
+
in an SPA, re-hydrate dynamic DOM (`useFreeday` in Vue/React does this).
|
|
45
|
+
8. Freeday owns components + tokens, **not layout**. Grids/stacks/one-off gaps come from our own
|
|
46
|
+
layout layer — build its theme on `var(--space-N)` so both systems stay in step.
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Adjust the paths if the package lives somewhere else (a workspace, a vendored copy, `wwwroot/` for
|
|
50
|
+
Blazor). Then verify the agent can actually read those files — an agent that can't open
|
|
51
|
+
`node_modules` will keep guessing.
|
|
52
|
+
|
|
53
|
+
## 2. What ships in the package
|
|
54
|
+
|
|
55
|
+
| File | What it answers |
|
|
56
|
+
|---|---|
|
|
57
|
+
| `COMPONENTS.md` | The complete class surface — what exists, its modifiers, minimal markup, a11y. |
|
|
58
|
+
| `USAGE.md` | The doctrine — which token/role/shadow/emphasis to use when. |
|
|
59
|
+
| `docs/getting-started.md` | Install + import + theme, per stack (Static HTML · Vue · React · Blazor). |
|
|
60
|
+
| `docs/integrations.md` | How to bridge third-party libraries (validation, charts, dates, i18n…). |
|
|
61
|
+
| `docs/reference-screen.html` | A full screen assembled from the shell down. Open it in a browser. |
|
|
62
|
+
| `docs/agent-onboarding.md` | This file. |
|
|
63
|
+
| `dist/` | Built CSS + enhancers. **`freeday.bundle.css` = tokens + components** (what `@cahyo-dimas/freeday/css` resolves to); `freeday.css` is components **only**, `freeday.tokens.css` tokens only — linking `freeday.css` alone leaves every `var(--…)` unresolved. Plus `freeday-*.js` and the `.d.ts` files. |
|
|
64
|
+
| `src/components/*.css` | The authoritative source for every class, when a doc is ambiguous. |
|
|
65
|
+
| `tokens/tokens.json` | Every token in W3C DTCG format — machine-readable. |
|
|
66
|
+
| `adapters/vue` · `adapters/react` · `adapters/blazor` | Typed wrappers, 10 components each. |
|
|
67
|
+
|
|
68
|
+
The live docs (with an interactive playground) are at
|
|
69
|
+
<https://cahyo-dimas.github.io/freeday-ui-kit/>, and the repo — including three complete example
|
|
70
|
+
apps under `examples/` (Vue, React, Blazor) that are **not** in the npm tarball — is at
|
|
71
|
+
<https://github.com/cahyo-dimas/freeday-ui-kit>.
|
|
72
|
+
|
|
73
|
+
## 3. Starting a new screen
|
|
74
|
+
|
|
75
|
+
The order matters; skipping to components is what produces flat, identical-card screens.
|
|
76
|
+
|
|
77
|
+
0. **Pick the screen shape first.** Which archetype is this — dashboard, master-detail, kanban,
|
|
78
|
+
wizard, POS…? The repo's
|
|
79
|
+
[`reference/README.md`](https://github.com/cahyo-dimas/freeday-ui-kit/blob/main/reference/README.md)
|
|
80
|
+
maps 15 archetypes to the exact primitives that compose each one, and says plainly which shapes
|
|
81
|
+
the kit has **no** component for (kanban columns, calendar month grid, chat bubbles, canvas) so
|
|
82
|
+
you build the frame instead of inventing a class. Not in the npm package — read it on GitHub.
|
|
83
|
+
1. **Shell** — is `.fdy-app` already in place (usually once, in the app layout)? If not, copy it
|
|
84
|
+
from `docs/getting-started.md` §The app shell.
|
|
85
|
+
2. **Theme** — `data-theme="light|dark"` + `data-density="comfortable|compact"` on `<html>`, set
|
|
86
|
+
once at the root. Use `compact` for table-heavy back-office screens.
|
|
87
|
+
3. **Fonts** — the package ships **no** `@font-face`. Load Sora / IBM Plex Sans / JetBrains Mono
|
|
88
|
+
yourself, or override `--font-display`/`--font-body`/`--font-mono`. Skipping this reads as
|
|
89
|
+
"unfinished design", not "missing dependency".
|
|
90
|
+
4. **Page frame** — `.fdy-page` + `.fdy-page__header` (eyebrow + `.fdy-title-page` + desc on the
|
|
91
|
+
left, **one** primary action on the right).
|
|
92
|
+
5. **Sections** — one `.fdy-page-section` per region, each with a `.fdy-title-section`.
|
|
93
|
+
6. **Components** — from `COMPONENTS.md`, inside the sections.
|
|
94
|
+
7. **Verify** — the checklist in §5.
|
|
95
|
+
|
|
96
|
+
## 4. Migrating an existing UI to Freeday
|
|
97
|
+
|
|
98
|
+
Migration is a **class-and-structure swap**, not a rewrite. Keep the app's DOM semantics; replace
|
|
99
|
+
the styling layer. Rough equivalents — always confirm the target class in `COMPONENTS.md`, and note
|
|
100
|
+
that Freeday deliberately has **no** layout/spacing utilities, so grid/flex/margin classes stay with
|
|
101
|
+
your own layout layer:
|
|
102
|
+
|
|
103
|
+
| Coming from | Freeday |
|
|
104
|
+
|---|---|
|
|
105
|
+
| `btn btn-primary` / `MudButton Variant=Filled` | `fdy-btn` |
|
|
106
|
+
| `btn btn-secondary` / `btn-outline-*` | `fdy-btn fdy-btn--ghost` |
|
|
107
|
+
| `btn btn-danger` | `fdy-btn fdy-btn--danger` |
|
|
108
|
+
| `btn btn-link` | `fdy-btn fdy-btn--text` |
|
|
109
|
+
| `btn-sm` / `btn-lg` | `fdy-btn--sm` / `fdy-btn--lg` |
|
|
110
|
+
| `form-control` / `MudTextField` | `fdy-field` + `fdy-label` + `fdy-input` |
|
|
111
|
+
| `form-select` / `<select>` / `MudSelect` | `fdy-combo` + `data-fdy-combo` (+ `freeday-select.js`) |
|
|
112
|
+
| `invalid-feedback` / `is-invalid` | `aria-invalid="true"` + `fdy-help fdy-help--error` |
|
|
113
|
+
| `input-group` / `input-group-text` | `fdy-input-group` + `__addon` / `__btn` |
|
|
114
|
+
| `form-check` / `form-switch` | `fdy-check` / `fdy-radio` / `fdy-switch` |
|
|
115
|
+
| `card` / `card-body` / `card-title` | `fdy-card` / `__body` / `__title` |
|
|
116
|
+
| `table table-striped` / `MudTable` | `fdy-table` in `fdy-table-wrap`; interactive → `fdy-datatable` |
|
|
117
|
+
| `badge bg-success` / `MudChip` (status) | `fdy-badge fdy-badge--success` |
|
|
118
|
+
| `alert alert-danger` | `fdy-alert fdy-alert--danger` + `role="alert"` |
|
|
119
|
+
| `modal` / `MudDialog` | `<dialog class="fdy-modal">` (native — drop the JS backdrop plumbing) |
|
|
120
|
+
| `offcanvas` / `MudDrawer` | `<dialog class="fdy-drawer">` + `data-fdy-drawer` |
|
|
121
|
+
| `nav nav-tabs` | `fdy-tabs` + `data-fdy-tabs` |
|
|
122
|
+
| `breadcrumb` / `pagination` | `fdy-breadcrumb` / `fdy-pagination` (same `<nav><ol>` structure) |
|
|
123
|
+
| `spinner-border` / `progress` | `fdy-spinner` / `fdy-progress` + `__bar` |
|
|
124
|
+
| `toast` container + JS | `Freeday.toast({…})` — imperative, no markup to author |
|
|
125
|
+
| `text-muted` | `fdy-text-muted` |
|
|
126
|
+
| `d-none` / `visually-hidden` | `fdy-hidden` / `fdy-visually-hidden` |
|
|
127
|
+
| `container` / `row` / `col-*` / `mb-3` / `gap-2` | **stays yours** — Freeday ships no layout utilities |
|
|
128
|
+
|
|
129
|
+
Order of work that avoids a half-migrated mess:
|
|
130
|
+
|
|
131
|
+
1. Load Freeday's CSS and **turn off the old framework's reset/preflight** — `base.css` is the
|
|
132
|
+
reset now. Two resets fighting is the usual source of "everything looks slightly off".
|
|
133
|
+
2. Shell + theme attributes first, so tokens resolve everywhere.
|
|
134
|
+
3. Then screen by screen: page frame → sections → controls. Convert a whole screen at a time;
|
|
135
|
+
half-converted screens can't be reviewed visually.
|
|
136
|
+
4. Delete the old framework's CSS only when no screen references it, then grep for leftover class
|
|
137
|
+
prefixes.
|
|
138
|
+
5. Replace hand-rolled modal/drawer/dropdown JS with the native-`<dialog>` components and the
|
|
139
|
+
enhancers — that is usually where the most code disappears.
|
|
140
|
+
|
|
141
|
+
## 5. Verification checklist (before claiming a screen is done)
|
|
142
|
+
|
|
143
|
+
- Every `fdy-*` class used appears in `COMPONENTS.md`. Grep the diff for `fdy-` and check.
|
|
144
|
+
- No raw hex/rgb/px in the diff's CSS. Grep for `#` and `px`.
|
|
145
|
+
- Exactly one `.fdy-btn` without a variant modifier on the screen; one `.fdy-title-page`.
|
|
146
|
+
- Toggle `data-theme="dark"` on `<html>` — nothing becomes unreadable, no hard-coded white/black.
|
|
147
|
+
- Toggle `data-density="compact"` — the layout still holds.
|
|
148
|
+
- Keyboard: Tab reaches every control, focus is always visible, Esc closes overlays.
|
|
149
|
+
- Form errors carry `aria-invalid` + a linked message; icon-only buttons have `aria-label`.
|
|
150
|
+
- The interactive components on the screen have their enhancer loaded, and SPA-rendered DOM is
|
|
151
|
+
re-hydrated.
|