@nim.zone/ui 0.6.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.
Files changed (129) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +681 -0
  3. package/dist/components/admin-shell.d.ts +121 -0
  4. package/dist/components/app-shell.d.ts +23 -0
  5. package/dist/components/auth-screen.d.ts +33 -0
  6. package/dist/components/avatar.d.ts +10 -0
  7. package/dist/components/badge.d.ts +14 -0
  8. package/dist/components/banner.d.ts +11 -0
  9. package/dist/components/breadcrumb.d.ts +12 -0
  10. package/dist/components/button.d.ts +21 -0
  11. package/dist/components/card.d.ts +15 -0
  12. package/dist/components/chat-composer.d.ts +52 -0
  13. package/dist/components/chat.d.ts +77 -0
  14. package/dist/components/checkout.d.ts +79 -0
  15. package/dist/components/choice.d.ts +15 -0
  16. package/dist/components/combobox.d.ts +31 -0
  17. package/dist/components/date-field.d.ts +58 -0
  18. package/dist/components/dialog.d.ts +20 -0
  19. package/dist/components/empty-state.d.ts +9 -0
  20. package/dist/components/feedback.d.ts +18 -0
  21. package/dist/components/field.d.ts +47 -0
  22. package/dist/components/icon-button.d.ts +12 -0
  23. package/dist/components/icon.d.ts +72 -0
  24. package/dist/components/layout.d.ts +13 -0
  25. package/dist/components/list.d.ts +16 -0
  26. package/dist/components/menu.d.ts +56 -0
  27. package/dist/components/onboarding.d.ts +50 -0
  28. package/dist/components/otp-input.d.ts +29 -0
  29. package/dist/components/pagination.d.ts +12 -0
  30. package/dist/components/password-field.d.ts +33 -0
  31. package/dist/components/phone-field.d.ts +44 -0
  32. package/dist/components/plan-card.d.ts +45 -0
  33. package/dist/components/plan-picker.d.ts +52 -0
  34. package/dist/components/profile-header.d.ts +47 -0
  35. package/dist/components/profile-screen.d.ts +44 -0
  36. package/dist/components/resource-meter.d.ts +22 -0
  37. package/dist/components/section-header.d.ts +8 -0
  38. package/dist/components/segmented.d.ts +18 -0
  39. package/dist/components/sheet.d.ts +18 -0
  40. package/dist/components/sign-in-flow.d.ts +60 -0
  41. package/dist/components/slider.d.ts +10 -0
  42. package/dist/components/stat.d.ts +10 -0
  43. package/dist/components/stepper.d.ts +17 -0
  44. package/dist/components/tab-bar.d.ts +40 -0
  45. package/dist/components/table.d.ts +33 -0
  46. package/dist/components/tabs.d.ts +23 -0
  47. package/dist/components/task-progress.d.ts +38 -0
  48. package/dist/components/theme.d.ts +49 -0
  49. package/dist/components/toast.d.ts +17 -0
  50. package/dist/components/tooltip.d.ts +18 -0
  51. package/dist/components/typography.d.ts +18 -0
  52. package/dist/components/wizard.d.ts +70 -0
  53. package/dist/index.d.ts +104 -0
  54. package/dist/lib/calendars.d.ts +81 -0
  55. package/dist/lib/cn.d.ts +7 -0
  56. package/dist/lib/countries.d.ts +33 -0
  57. package/dist/lib/use-anchor.d.ts +22 -0
  58. package/dist/nim.css +1 -0
  59. package/dist/nim.js +3266 -0
  60. package/package.json +83 -0
  61. package/src/components/admin-shell.tsx +338 -0
  62. package/src/components/app-shell.tsx +36 -0
  63. package/src/components/auth-screen.tsx +71 -0
  64. package/src/components/avatar.tsx +32 -0
  65. package/src/components/badge.tsx +44 -0
  66. package/src/components/banner.tsx +47 -0
  67. package/src/components/breadcrumb.tsx +45 -0
  68. package/src/components/button.tsx +64 -0
  69. package/src/components/card.tsx +46 -0
  70. package/src/components/chat-composer.tsx +331 -0
  71. package/src/components/chat.tsx +352 -0
  72. package/src/components/checkout.tsx +173 -0
  73. package/src/components/choice.tsx +46 -0
  74. package/src/components/combobox.tsx +157 -0
  75. package/src/components/date-field.tsx +394 -0
  76. package/src/components/dialog.tsx +77 -0
  77. package/src/components/empty-state.tsx +23 -0
  78. package/src/components/feedback.tsx +61 -0
  79. package/src/components/field.tsx +177 -0
  80. package/src/components/icon-button.tsx +35 -0
  81. package/src/components/icon.tsx +167 -0
  82. package/src/components/layout.tsx +38 -0
  83. package/src/components/list.tsx +78 -0
  84. package/src/components/menu.tsx +172 -0
  85. package/src/components/onboarding.tsx +157 -0
  86. package/src/components/otp-input.tsx +144 -0
  87. package/src/components/pagination.tsx +86 -0
  88. package/src/components/password-field.tsx +112 -0
  89. package/src/components/phone-field.tsx +242 -0
  90. package/src/components/plan-card.tsx +124 -0
  91. package/src/components/plan-picker.tsx +144 -0
  92. package/src/components/profile-header.tsx +134 -0
  93. package/src/components/profile-screen.tsx +104 -0
  94. package/src/components/resource-meter.tsx +60 -0
  95. package/src/components/section-header.tsx +31 -0
  96. package/src/components/segmented.tsx +53 -0
  97. package/src/components/sheet.tsx +74 -0
  98. package/src/components/sign-in-flow.tsx +322 -0
  99. package/src/components/slider.tsx +50 -0
  100. package/src/components/stat.tsx +30 -0
  101. package/src/components/stepper.tsx +77 -0
  102. package/src/components/tab-bar.tsx +82 -0
  103. package/src/components/table.tsx +81 -0
  104. package/src/components/tabs.tsx +72 -0
  105. package/src/components/task-progress.tsx +111 -0
  106. package/src/components/theme.tsx +112 -0
  107. package/src/components/toast.tsx +87 -0
  108. package/src/components/tooltip.tsx +30 -0
  109. package/src/components/typography.tsx +65 -0
  110. package/src/components/wizard.tsx +192 -0
  111. package/src/index.ts +216 -0
  112. package/src/lib/calendars.ts +227 -0
  113. package/src/lib/cn.ts +8 -0
  114. package/src/lib/countries.ts +110 -0
  115. package/src/lib/use-anchor.ts +92 -0
  116. package/src/theme/colorways/coral.css +62 -0
  117. package/src/theme/colorways/oxblood.css +23 -0
  118. package/src/theme/colorways/paper.css +68 -0
  119. package/src/theme/colorways/teal.css +56 -0
  120. package/src/theme/colorways/vermilion.css +21 -0
  121. package/src/theme/components.css +4606 -0
  122. package/src/theme/contract.css +204 -0
  123. package/src/theme/fonts.css +67 -0
  124. package/src/theme/index.css +33 -0
  125. package/src/theme/persian.css +47 -0
  126. package/src/theme/reset.css +118 -0
  127. package/src/theme/styles/ledger.css +106 -0
  128. package/src/theme/styles/vlora.css +91 -0
  129. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,204 @@
1
+ /* ═══════════════════════════════════════════════════════════════════════════
2
+ nim — TOKEN CONTRACT
3
+
4
+ This file declares the vocabulary every nim component is allowed to speak.
5
+ Components reference these names and nothing else; a theme file is the only
6
+ place a literal colour, radius, shadow, or type value may appear.
7
+
8
+ The contract is split in two:
9
+
10
+ · Invariants (below) — scale, rhythm, motion, geometry of touch. These are
11
+ ergonomic facts rather than brand decisions, so they are identical in
12
+ every theme and are set here once.
13
+ · Personality (themes/*.css) — colour, shape, elevation, and type voice.
14
+ A theme MUST define every `--nim-*` name listed in the "required of a
15
+ theme" block at the bottom of this file, or components will fall back to
16
+ an unstyled value.
17
+
18
+ Naming: `--nim-<role>-<modifier>`. Roles are semantic (canvas, surface, ink,
19
+ line, accent), never literal (never `--nim-orange`, never `--nim-gray-300`).
20
+ ═══════════════════════════════════════════════════════════════════════════ */
21
+
22
+ /* ═══════════════════════════════════════════════════════════════════════════
23
+ SCHEME
24
+
25
+ Every colour in a colourway is a `light-dark()` pair, and `color-scheme` is
26
+ what picks a side. Setting it here means a colourway is ONE block: no
27
+ duplicated dark rule, no `prefers-color-scheme` query per palette, and no
28
+ hand-maintained exclusion list to fall behind — which is exactly how a theme
29
+ ended up inheriting another one's dark palette before 0.2.
30
+
31
+ Unset follows the OS. `data-nim-scheme` pins it.
32
+ ═══════════════════════════════════════════════════════════════════════════ */
33
+
34
+ :where(:root),
35
+ :where(.nim-root) {
36
+ color-scheme: light dark;
37
+ }
38
+
39
+ [data-nim-scheme='light'] {
40
+ color-scheme: light;
41
+ }
42
+
43
+ [data-nim-scheme='dark'] {
44
+ color-scheme: dark;
45
+ }
46
+
47
+ :root {
48
+ /* ─── Space ────────────────────────────────────────────────────────────
49
+ A 4px base. Components use steps, never raw pixels, so density can be
50
+ retuned globally by overriding this scale alone. */
51
+ --nim-space-0: 0px;
52
+ --nim-space-1: 4px;
53
+ --nim-space-2: 8px;
54
+ --nim-space-3: 12px;
55
+ --nim-space-4: 16px;
56
+ --nim-space-5: 20px;
57
+ --nim-space-6: 24px;
58
+ --nim-space-7: 32px;
59
+ --nim-space-8: 40px;
60
+ --nim-space-9: 48px;
61
+ --nim-space-10: 64px;
62
+ --nim-space-11: 80px;
63
+ --nim-space-12: 104px;
64
+
65
+ /* ─── Type scale ───────────────────────────────────────────────────────
66
+ Absolute sizes, named by their pixel value at the browser default. The
67
+ composite `--nim-type-*` roles in a theme are built from these. */
68
+ --nim-text-11: 0.6875rem;
69
+ --nim-text-12: 0.75rem;
70
+ --nim-text-13: 0.8125rem;
71
+ --nim-text-14: 0.875rem;
72
+ --nim-text-15: 0.9375rem;
73
+ --nim-text-16: 1rem;
74
+ --nim-text-18: 1.125rem;
75
+ --nim-text-20: 1.25rem;
76
+ --nim-text-24: 1.5rem;
77
+ --nim-text-28: 1.75rem;
78
+ --nim-text-32: 2rem;
79
+ --nim-text-40: 2.5rem;
80
+
81
+ --nim-weight-regular: 400;
82
+ --nim-weight-medium: 500;
83
+ --nim-weight-semibold: 600;
84
+ --nim-weight-bold: 700;
85
+
86
+ /* ─── Motion ───────────────────────────────────────────────────────────
87
+ Three durations and three curves is the whole budget. `spring` overshoots
88
+ and is reserved for elements that enter; `standard` is everything else. */
89
+ --nim-dur-fast: 140ms;
90
+ --nim-dur-base: 240ms;
91
+ --nim-dur-slow: 400ms;
92
+ --nim-ease-standard: cubic-bezier(0.25, 0.46, 0.45, 0.94);
93
+ --nim-ease-decelerate: cubic-bezier(0, 0, 0.2, 1);
94
+ --nim-ease-spring: cubic-bezier(0.34, 1.5, 0.64, 1);
95
+
96
+ /* ─── Density ──────────────────────────────────────────────────────────
97
+ One multiplier retunes the whole interface at once. It scales the control
98
+ heights below and the block padding of anything row-shaped (list rows,
99
+ table cells), so a data-dense screen and a mobile flow stay the same
100
+ system rather than the same components at hand-tuned sizes.
101
+
102
+ Set it on any subtree: `style="--nim-density: 0.82"`. It never scales
103
+ type, and it never scales `--nim-touch-min`, which is a floor.
104
+
105
+ The multiplication lives in `components.css`, at each place a height is
106
+ used — `calc(var(--nim-control-md) * var(--nim-density))` — and not in the
107
+ control tokens below. A custom property that references another custom
108
+ property is substituted where it is DECLARED, so folding density into
109
+ `--nim-control-md` here would freeze it at the root's value and a subtree
110
+ override would silently do nothing. */
111
+ --nim-density: 1;
112
+
113
+ /* ─── Control geometry ─────────────────────────────────────────────────
114
+ Heights are shared across every control so a button, an input, and a
115
+ select line up on the same row without per-component nudging. 44px is the
116
+ minimum touch target and is never reduced, only visually inset. */
117
+ --nim-control-sm: 36px;
118
+ --nim-control-md: 44px;
119
+ --nim-control-lg: 52px;
120
+ --nim-touch-min: 44px;
121
+ --nim-border-width: 1px;
122
+
123
+ /* ─── Fixed sizes ──────────────────────────────────────────────────────
124
+ Geometry that is neither a space step nor a control height: the diameter
125
+ of a dot, the side of an avatar, the thickness of a track. These lived as
126
+ literals inside `components.css` until 0.2, which put them outside the
127
+ contract — a theme could not answer them, so an icon set drawn at a
128
+ different optical weight had no way in. */
129
+ --nim-size-dot: 6px;
130
+ --nim-size-icon-xs: 14px;
131
+ --nim-size-icon-sm: 16px;
132
+ --nim-size-icon-md: 20px;
133
+ --nim-size-icon-lg: 24px;
134
+ --nim-size-icon-xl: 32px;
135
+ --nim-size-avatar-sm: 28px;
136
+ --nim-size-avatar-md: 40px;
137
+ --nim-size-avatar-lg: 56px;
138
+ --nim-size-track: 6px;
139
+ --nim-size-handle: 36px;
140
+ --nim-size-handle-thickness: 4px;
141
+ --nim-size-focus-ring: 2px;
142
+ --nim-size-focus-offset: 2px;
143
+
144
+ /* ─── Layout ───────────────────────────────────────────────────────────
145
+ `frame` is the mobile app frame on a desktop viewport; `content` is a
146
+ comfortable column; `measure` is the reading measure for prose. */
147
+ --nim-frame-max: 768px;
148
+ --nim-content-max: 680px;
149
+ --nim-measure: 34rem;
150
+ --nim-safe-top: env(safe-area-inset-top, 0px);
151
+ --nim-safe-bottom: env(safe-area-inset-bottom, 0px);
152
+
153
+ /* ─── Z layers ─────────────────────────────────────────────────────────
154
+ Named so no component invents its own number. */
155
+ --nim-z-sticky: 20;
156
+ --nim-z-nav: 40;
157
+ --nim-z-scrim: 60;
158
+ --nim-z-sheet: 70;
159
+ --nim-z-toast: 90;
160
+ }
161
+
162
+ /* ═══════════════════════════════════════════════════════════════════════════
163
+ REQUIRED OF A STYLE · REQUIRED OF A COLOURWAY
164
+
165
+ nim has two independent axes, and each answers half of this list. A style
166
+ owns how the interface is SHAPED; a colourway owns how it is COLOURED. That
167
+ split is why `oxblood` is six declarations rather than a 220-line theme, and
168
+ why a new palette cannot accidentally change a radius.
169
+
170
+ Both are listed here as documentation — deliberately without values, so a
171
+ missing token fails loudly in review rather than silently inheriting a
172
+ neutral grey.
173
+
174
+ ── A STYLE must define (`styles/*.css`) ───────────────────────────────
175
+ Shape --nim-radius-xs · -sm · -md · -lg · -xl · -pill
176
+ Elevation --nim-shadow-sm · -md · -lg (geometry; drawn in the
177
+ colourway's --nim-shadow-tint-*)
178
+ --nim-shadow-focus · --nim-shadow-focus-danger
179
+ Type --nim-font-sans · --nim-font-mono · --nim-font-numeric
180
+ --nim-type-display-* · --nim-type-title-* · --nim-type-body-*
181
+ --nim-type-control-* · --nim-type-label-* · --nim-type-caption-*
182
+ --nim-tracking-tight · -normal · -wide
183
+ --nim-leading-base · -tight · -normal · -relaxed
184
+ Voice --nim-label-transform · --nim-label-tracking
185
+ --nim-press-scale · --nim-press-translate
186
+
187
+ ── A COLOURWAY must define (`colorways/*.css`) ────────────────────────
188
+ Every value as a `light-dark()` pair.
189
+
190
+ Surfaces --nim-canvas · --nim-canvas-sunken · --nim-surface
191
+ --nim-surface-raised · --nim-surface-muted · --nim-scrim
192
+ Ink --nim-ink · --nim-ink-secondary · --nim-ink-tertiary
193
+ --nim-ink-inverse · --nim-on-accent
194
+ Lines --nim-line · --nim-line-soft · --nim-line-strong
195
+ Accent --nim-accent · --nim-accent-hover · --nim-accent-strong
196
+ --nim-accent-soft · --nim-accent-line
197
+ Status --nim-success · --nim-success-soft · --nim-warning
198
+ --nim-warning-soft · --nim-danger · --nim-danger-hover
199
+ --nim-danger-soft · --nim-info · --nim-info-soft
200
+ Elevation --nim-shadow-tint-sm · -md · -lg
201
+
202
+ Colourways that share a neutral half state it once under a grouped
203
+ selector — see `colorways/paper.css`.
204
+ ═══════════════════════════════════════════════════════════════════════════ */
@@ -0,0 +1,67 @@
1
+ /* ═══════════════════════════════════════════════════════════════════════════
2
+ nim — VAZIRMATN (optional)
3
+
4
+ The Persian face used across this repo's Farsi products (vlora-app,
5
+ vlora-web, vlora-admin, iranianlawclub-web). It is NOT part of
6
+ `nim/styles.css`: a stylesheet that requests font files the host does not
7
+ serve produces four 404s and a flash of fallback, so loading the face is an
8
+ explicit decision.
9
+
10
+ import 'nim/fonts.css'
11
+
12
+ Then serve the three subsets at `/fonts/` — the convention every Farsi app
13
+ in this repo already follows:
14
+
15
+ public/fonts/vazirmatn-arabic.woff2
16
+ public/fonts/vazirmatn-latin-ext.woff2
17
+ public/fonts/vazirmatn-latin.woff2
18
+
19
+ An app that serves them elsewhere skips this file and declares its own
20
+ `@font-face`; nothing else in the kit depends on it. `--nim-font-sans` on
21
+ the `vlora` style already names 'Vazirmatn' first, so the face is picked up
22
+ as soon as it loads.
23
+
24
+ One variable file per subset, weight 100–900, so a single request covers
25
+ every weight the kit uses. Vazirmatn is licensed under the SIL Open Font
26
+ License 1.1.
27
+ ═══════════════════════════════════════════════════════════════════════════ */
28
+
29
+ /* Arabic subset — every Persian/Arabic character, all weights. */
30
+ @font-face {
31
+ font-family: 'Vazirmatn';
32
+ font-style: normal;
33
+ font-weight: 100 900;
34
+ font-display: swap;
35
+ src: url('/fonts/vazirmatn-arabic.woff2') format('woff2');
36
+ unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF,
37
+ U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC,
38
+ U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC4, U+10EFC-10EFF, U+1EE00-1EE03, U+1EE05-1EE1F,
39
+ U+1EE21-1EE22, U+1EE24, U+1EE27, U+1EE29-1EE32, U+1EE34-1EE37, U+1EE39, U+1EE3B, U+1EE42,
40
+ U+1EE47, U+1EE49, U+1EE4B, U+1EE4D-1EE4F, U+1EE51-1EE52, U+1EE54, U+1EE57, U+1EE59,
41
+ U+1EE5B, U+1EE5D, U+1EE5F, U+1EE61-1EE62, U+1EE64, U+1EE67-1EE6A, U+1EE6C-1EE72,
42
+ U+1EE74-1EE77, U+1EE79-1EE7C, U+1EE7E, U+1EE80-1EE89, U+1EE8B-1EE9B, U+1EEA1-1EEA3,
43
+ U+1EEA5-1EEA9, U+1EEAB-1EEBB, U+1EEF0-1EEF1;
44
+ }
45
+
46
+ /* Latin-ext subset, all weights. */
47
+ @font-face {
48
+ font-family: 'Vazirmatn';
49
+ font-style: normal;
50
+ font-weight: 100 900;
51
+ font-display: swap;
52
+ src: url('/fonts/vazirmatn-latin-ext.woff2') format('woff2');
53
+ unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
54
+ U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
55
+ U+2113, U+2C60-2C7F, U+A720-A7FF;
56
+ }
57
+
58
+ /* Latin subset, all weights. */
59
+ @font-face {
60
+ font-family: 'Vazirmatn';
61
+ font-style: normal;
62
+ font-weight: 100 900;
63
+ font-display: swap;
64
+ src: url('/fonts/vazirmatn-latin.woff2') format('woff2');
65
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
66
+ U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
67
+ }
@@ -0,0 +1,33 @@
1
+ /* ═══════════════════════════════════════════════════════════════════════════
2
+ nim — stylesheet entry
3
+
4
+ Import order is load-bearing:
5
+ 1. the contract — invariants every component relies on, and the
6
+ `color-scheme` switch that resolves `light-dark()`
7
+ 2. styles — shape, elevation geometry, type voice, press
8
+ 3. colourways — surfaces, ink, lines, accent, status, shadow tint
9
+ 4. persian — script corrections that must outrank a style
10
+ 5. reset — scoped to `.nim-root`, never global
11
+ 6. components — the only file that draws anything
12
+
13
+ Style and colourway are two independent axes. A product that wants one
14
+ pairing can import `contract.css`, its style, its colourway, `reset.css`
15
+ and `components.css` directly and skip the rest.
16
+ ═══════════════════════════════════════════════════════════════════════════ */
17
+
18
+ @import './contract.css';
19
+
20
+ @import './styles/ledger.css';
21
+ @import './styles/vlora.css';
22
+
23
+ @import './colorways/paper.css';
24
+ @import './colorways/vermilion.css';
25
+ @import './colorways/oxblood.css';
26
+ @import './colorways/coral.css';
27
+ @import './colorways/teal.css';
28
+
29
+ /* Script corrections come after the styles so they win on source order. */
30
+ @import './persian.css';
31
+
32
+ @import './reset.css';
33
+ @import './components.css';
@@ -0,0 +1,47 @@
1
+ /* ═══════════════════════════════════════════════════════════════════════════
2
+ nim — PERSIAN / ARABIC SCRIPT CORRECTIONS
3
+
4
+ Two of nim's type tokens are actively harmful to Persian, and neither is a
5
+ style's fault — they are correct for Latin and wrong for a joined script:
6
+
7
+ · `--nim-label-tracking` (0.12em on the ledger style) pulls apart letters
8
+ that must stay connected. Tracking a Persian word does not space it out,
9
+ it breaks it.
10
+ · negative tracking on display and title sizes does the same thing more
11
+ subtly.
12
+
13
+ `text-transform: uppercase` is a no-op on Persian glyphs, but it still
14
+ shouts at any Latin mixed into the same label, so it goes too.
15
+
16
+ Scoped to the LANGUAGE, not the direction: `dir="rtl"` says which way the
17
+ line runs, `lang="fa"` says which script is being set, and only the second
18
+ one implies these corrections. An RTL page of Latin text wants none of them.
19
+
20
+ Unlayered and imported after the styles, so it wins on source order rather
21
+ than by inflating specificity — a style could otherwise re-break it.
22
+ ═══════════════════════════════════════════════════════════════════════════ */
23
+
24
+ :is(:lang(fa), :lang(ar), [lang='fa'], [lang='ar']) {
25
+ --nim-label-transform: none;
26
+ --nim-label-tracking: 0;
27
+ --nim-tracking-tight: 0;
28
+ --nim-tracking-normal: 0;
29
+ --nim-tracking-wide: 0;
30
+ --nim-type-display-tracking: 0;
31
+ --nim-type-control-tracking: 0;
32
+ --nim-type-control-transform: none;
33
+
34
+ /* `calt` and `kern` shape the joins; `ss01` is Vazirmatn's Persian-preferred
35
+ forms (the four-dot heh, the Persian kaf and yeh). */
36
+ font-feature-settings: 'calt' 1, 'kern' 1, 'ss01' 1;
37
+ word-spacing: normal;
38
+ }
39
+
40
+ /* Persian text is read at a looser rhythm than Latin at the same size. The
41
+ ledger style is set for Latin, so a Farsi page on it gets the room the
42
+ vlora style already builds in. */
43
+ :is(:lang(fa), :lang(ar), [lang='fa'], [lang='ar']):is([data-nim-style='ledger'], :not([data-nim-style])) {
44
+ --nim-leading-base: 1.7;
45
+ --nim-leading-tight: 1.35;
46
+ --nim-leading-relaxed: 1.9;
47
+ }
@@ -0,0 +1,118 @@
1
+ /* ═══════════════════════════════════════════════════════════════════════════
2
+ nim — RESET
3
+ Only what the kit depends on. It is deliberately small: nim is meant to drop
4
+ into an existing app without repainting that app's own markup.
5
+ ═══════════════════════════════════════════════════════════════════════════ */
6
+
7
+ @layer base {
8
+ *,
9
+ *::before,
10
+ *::after {
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ .nim-root,
15
+ .nim-root * {
16
+ margin: 0;
17
+ }
18
+
19
+ /* The scoped root. Applying it to <body> themes the whole app; applying it
20
+ to a subtree lets nim live beside another design system. */
21
+ .nim-root {
22
+ background: var(--nim-canvas);
23
+ color: var(--nim-ink);
24
+ font-family: var(--nim-font-sans);
25
+ font-size: var(--nim-type-body-size);
26
+ line-height: var(--nim-type-body-line);
27
+ letter-spacing: var(--nim-tracking-normal);
28
+ text-rendering: optimizeLegibility;
29
+ -webkit-font-smoothing: antialiased;
30
+ -moz-osx-font-smoothing: grayscale;
31
+ }
32
+
33
+ /* Portalled surfaces mount on <body>, outside `.nim-root` — so they inherit
34
+ the document's default serif rather than the kit's typography. Every
35
+ component that portals is listed here and given the same ground the root
36
+ establishes. Missing this is invisible in Latin at a glance and glaring in
37
+ Persian, where the fallback has no matching face at all. */
38
+ .nim-sheet__panel,
39
+ .nim-menu,
40
+ .nim-popover,
41
+ .nim-toast-stack {
42
+ color: var(--nim-ink);
43
+ font-family: var(--nim-font-sans);
44
+ font-size: var(--nim-type-body-size);
45
+ line-height: var(--nim-type-body-line);
46
+ letter-spacing: var(--nim-tracking-normal);
47
+ text-rendering: optimizeLegibility;
48
+ -webkit-font-smoothing: antialiased;
49
+ -moz-osx-font-smoothing: grayscale;
50
+ }
51
+
52
+ .nim-sheet__panel :is(input, button, textarea, select),
53
+ .nim-menu :is(input, button, textarea, select),
54
+ .nim-popover :is(input, button, textarea, select) {
55
+ font: inherit;
56
+ color: inherit;
57
+ }
58
+
59
+ .nim-root img,
60
+ .nim-root svg,
61
+ .nim-root video,
62
+ .nim-root canvas {
63
+ display: block;
64
+ max-inline-size: 100%;
65
+ }
66
+
67
+ .nim-root :is(input, button, textarea, select) {
68
+ font: inherit;
69
+ color: inherit;
70
+ letter-spacing: inherit;
71
+ }
72
+
73
+ .nim-root :is(ul, ol):where([class*='nim-']) {
74
+ list-style: none;
75
+ padding: 0;
76
+ }
77
+
78
+ /* One focus treatment for the entire kit, drawn outside the box so it never
79
+ shifts layout and never gets clipped by an overflow container. */
80
+ .nim-root :focus-visible {
81
+ outline: none;
82
+ box-shadow: var(--nim-shadow-focus);
83
+ }
84
+
85
+ .nim-root ::selection {
86
+ background: var(--nim-accent);
87
+ color: var(--nim-on-accent);
88
+ }
89
+
90
+ /* Numerals are tabular wherever nim prints a figure, so columns of values
91
+ line up and a counting number never reflows its neighbours. */
92
+ .nim-root :is(.nim-stat__value, .nim-badge, time, [data-nim-numeric]) {
93
+ font-variant-numeric: tabular-nums;
94
+ }
95
+
96
+ @media (prefers-reduced-motion: reduce) {
97
+ .nim-root *,
98
+ .nim-root *::before,
99
+ .nim-root *::after {
100
+ animation-duration: 0.01ms !important;
101
+ animation-iteration-count: 1 !important;
102
+ transition-duration: 0.01ms !important;
103
+ }
104
+ }
105
+ }
106
+
107
+ @layer utilities {
108
+ .nim-visually-hidden {
109
+ position: absolute;
110
+ inline-size: 1px;
111
+ block-size: 1px;
112
+ padding: 0;
113
+ overflow: hidden;
114
+ clip-path: inset(50%);
115
+ white-space: nowrap;
116
+ border: 0;
117
+ }
118
+ }
@@ -0,0 +1,106 @@
1
+ /* ═══════════════════════════════════════════════════════════════════════════
2
+ STYLE · LEDGER — the nim house style
3
+
4
+ A style owns SHAPE, ELEVATION GEOMETRY, TYPE VOICE and PRESS. It owns no
5
+ colour at all: every value below that looks coloured is a reference to a
6
+ token the active colourway answers. Swapping the colourway under this style
7
+ changes the palette and nothing else; swapping the style changes the
8
+ physics.
9
+
10
+ Ledger: square corners, hairline rules, hard offset shadows like a print
11
+ register mark, mono type for anything machine-shaped, and a press that
12
+ shifts into the shadow — a key being struck, not a surface being squeezed.
13
+
14
+ Selected with `data-nim-style="ledger"`, and used when no style is set.
15
+ `:where(:root)` contributes no specificity, so the bare-root default never
16
+ outranks an explicit style set further down the tree.
17
+ ═══════════════════════════════════════════════════════════════════════════ */
18
+
19
+ :where(:root),
20
+ [data-nim-style='ledger'] {
21
+ /* ─── Shape ──────────────────────────────────────────────────────────
22
+ Square. Nothing rounds "a little bit"; only genuinely pill-shaped
23
+ affordances (the status dot, the toggle track) use `pill`. */
24
+ --nim-radius-xs: 0px;
25
+ --nim-radius-sm: 0px;
26
+ --nim-radius-md: 0px;
27
+ --nim-radius-lg: 0px;
28
+ --nim-radius-xl: 0px;
29
+ --nim-radius-pill: 999px;
30
+
31
+ /* ─── Elevation ──────────────────────────────────────────────────────
32
+ Paper does not float. Depth is a hard offset and a control presses into
33
+ it rather than lifting off it. The offsets are the style's; the colour
34
+ they are drawn in is the colourway's. */
35
+ --nim-shadow-sm: 0 1px 0 var(--nim-shadow-tint-sm);
36
+ --nim-shadow-md: 3px 3px 0 var(--nim-shadow-tint-md);
37
+ --nim-shadow-lg: 6px 6px 0 var(--nim-shadow-tint-lg);
38
+
39
+ /* A ring drawn outside the box, on the canvas, so it reads on any surface.
40
+ An invalid field focuses in danger, so the ring never contradicts the
41
+ message printed under it. */
42
+ --nim-shadow-focus: 0 0 0 2px var(--nim-canvas), 0 0 0 4px var(--nim-accent);
43
+ --nim-shadow-focus-danger: 0 0 0 2px var(--nim-canvas), 0 0 0 4px var(--nim-danger);
44
+
45
+ /* ─── Type ───────────────────────────────────────────────────────────
46
+ One sans for substance, one mono for anything machine-shaped. An app
47
+ with its own brand face overrides these two on its provider; the rest of
48
+ the scale is stated in relative terms and follows. */
49
+ --nim-font-sans: 'Geist', 'Vazirmatn', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
50
+ --nim-font-mono: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
51
+ --nim-font-numeric: var(--nim-font-mono);
52
+
53
+ --nim-tracking-tight: -0.026em;
54
+ --nim-tracking-normal: -0.008em;
55
+ --nim-tracking-wide: 0.12em;
56
+
57
+ /* `base` is this style's resting leading; tight and relaxed are stated
58
+ against it, which is what keeps the three names ordered in every style
59
+ rather than only in this one. */
60
+ --nim-leading-base: 1.45;
61
+ --nim-leading-tight: 1.12;
62
+ --nim-leading-normal: var(--nim-leading-base);
63
+ --nim-leading-relaxed: 1.66;
64
+
65
+ --nim-type-display-size: clamp(2.25rem, 6vw, 3.25rem);
66
+ --nim-type-display-line: 1;
67
+ --nim-type-display-weight: var(--nim-weight-medium);
68
+ --nim-type-display-tracking: -0.036em;
69
+ --nim-type-display-family: var(--nim-font-sans);
70
+
71
+ --nim-type-title-lg-size: var(--nim-text-24);
72
+ --nim-type-title-lg-line: var(--nim-leading-tight);
73
+ --nim-type-title-lg-weight: var(--nim-weight-medium);
74
+ --nim-type-title-md-size: var(--nim-text-18);
75
+ --nim-type-title-md-line: 1.25;
76
+ --nim-type-title-md-weight: var(--nim-weight-medium);
77
+
78
+ --nim-type-body-size: var(--nim-text-16);
79
+ --nim-type-body-line: var(--nim-leading-relaxed);
80
+ --nim-type-body-weight: var(--nim-weight-regular);
81
+ --nim-type-body-sm-size: var(--nim-text-14);
82
+
83
+ /* Labels are set as rules: mono, uppercase, wide-tracked. This pair of
84
+ tokens is most of what makes the style sound like a ledger. */
85
+ --nim-type-label-size: var(--nim-text-12);
86
+ --nim-type-label-weight: var(--nim-weight-medium);
87
+ --nim-type-label-family: var(--nim-font-mono);
88
+ --nim-label-transform: uppercase;
89
+ --nim-label-tracking: var(--nim-tracking-wide);
90
+
91
+ /* A label is a legend printed beside a thing; a control is a thing you
92
+ press. Controls do not borrow the label voice. */
93
+ --nim-type-control-size: var(--nim-text-14);
94
+ --nim-type-control-family: var(--nim-font-sans);
95
+ --nim-type-control-weight: var(--nim-weight-medium);
96
+ --nim-type-control-tracking: var(--nim-tracking-normal);
97
+ --nim-type-control-transform: none;
98
+
99
+ --nim-type-caption-size: var(--nim-text-12);
100
+ --nim-type-caption-line: 1.5;
101
+
102
+ /* ─── Voice ──────────────────────────────────────────────────────────
103
+ How a control answers a press. */
104
+ --nim-press-scale: 1;
105
+ --nim-press-translate: 2px;
106
+ }
@@ -0,0 +1,91 @@
1
+ /* ═══════════════════════════════════════════════════════════════════════════
2
+ STYLE · VLORA — soft, rounded, Persian-first
3
+
4
+ Ledger's opposite on every axis a style owns: generous radii, soft ambient
5
+ shadows, sentence-case labels, looser leading, and a press that compresses
6
+ rather than strikes. Carried over from `vlora-app` so an app on this style
7
+ keeps its exact feel while moving onto nim's contract.
8
+
9
+ Like every style it owns no colour. Pair it with the `coral` colourway for
10
+ Vlora's palette or `teal` for Fatemifar's.
11
+
12
+ Selected with `data-nim-style="vlora"`.
13
+ ═══════════════════════════════════════════════════════════════════════════ */
14
+
15
+ [data-nim-style='vlora'] {
16
+ /* ─── Shape ──────────────────────────────────────────────────────────
17
+ Rounded and soft. This block is most of what separates the two styles. */
18
+ --nim-radius-xs: 6px;
19
+ --nim-radius-sm: 8px;
20
+ --nim-radius-md: 12px;
21
+ --nim-radius-lg: 20px;
22
+ --nim-radius-xl: 24px;
23
+ --nim-radius-pill: 999px;
24
+
25
+ /* ─── Elevation ────────────────────────────────────────────────────── */
26
+ --nim-shadow-sm: 0 1px 4px var(--nim-shadow-tint-sm);
27
+ --nim-shadow-md: 0 4px 14px var(--nim-shadow-tint-md);
28
+ --nim-shadow-lg: 0 8px 28px var(--nim-shadow-tint-lg);
29
+
30
+ /* A soft halo rather than a drawn ring, mixed from whatever accent the
31
+ colourway supplies. */
32
+ --nim-shadow-focus: 0 0 0 3px color-mix(in srgb, var(--nim-accent) 24%, transparent);
33
+ --nim-shadow-focus-danger: 0 0 0 3px color-mix(in srgb, var(--nim-danger) 24%, transparent);
34
+
35
+ /* ─── Type ───────────────────────────────────────────────────────────
36
+ Persian-first: Vazirmatn leads. An app with its own face — Fatemifar's
37
+ YekanBakh, say — overrides these two names on its provider. */
38
+ --nim-font-sans: 'Vazirmatn', system-ui, -apple-system, sans-serif;
39
+ --nim-font-mono: 'Vazirmatn', ui-monospace, SFMono-Regular, monospace;
40
+ --nim-font-numeric: var(--nim-font-sans);
41
+
42
+ --nim-tracking-tight: -0.018em;
43
+ --nim-tracking-normal: -0.012em;
44
+ --nim-tracking-wide: -0.004em;
45
+
46
+ /* Persian needs the room, so every rung sits higher than Ledger's — but the
47
+ three stay ordered, which they were not before 0.2. */
48
+ --nim-leading-base: 1.84;
49
+ --nim-leading-tight: 1.48;
50
+ --nim-leading-normal: var(--nim-leading-base);
51
+ --nim-leading-relaxed: 1.96;
52
+
53
+ --nim-type-display-size: clamp(2.25rem, 6vw, 3.5rem);
54
+ --nim-type-display-line: 1.48;
55
+ --nim-type-display-weight: var(--nim-weight-semibold);
56
+ --nim-type-display-tracking: -0.03em;
57
+ --nim-type-display-family: var(--nim-font-sans);
58
+
59
+ --nim-type-title-lg-size: var(--nim-text-24);
60
+ --nim-type-title-lg-line: 1.76;
61
+ --nim-type-title-lg-weight: var(--nim-weight-semibold);
62
+ --nim-type-title-md-size: var(--nim-text-20);
63
+ --nim-type-title-md-line: 1.84;
64
+ --nim-type-title-md-weight: var(--nim-weight-semibold);
65
+
66
+ --nim-type-body-size: var(--nim-text-16);
67
+ --nim-type-body-line: var(--nim-leading-relaxed);
68
+ --nim-type-body-weight: var(--nim-weight-regular);
69
+ --nim-type-body-sm-size: var(--nim-text-14);
70
+
71
+ /* Persian has no case, and letterspacing damages its joined forms, so this
72
+ style neither uppercases nor tracks its labels. */
73
+ --nim-type-label-size: var(--nim-text-12);
74
+ --nim-type-label-weight: var(--nim-weight-medium);
75
+ --nim-type-label-family: var(--nim-font-sans);
76
+ --nim-label-transform: none;
77
+ --nim-label-tracking: var(--nim-tracking-wide);
78
+
79
+ --nim-type-control-size: var(--nim-text-15);
80
+ --nim-type-control-family: var(--nim-font-sans);
81
+ --nim-type-control-weight: var(--nim-weight-medium);
82
+ --nim-type-control-tracking: var(--nim-tracking-normal);
83
+ --nim-type-control-transform: none;
84
+
85
+ --nim-type-caption-size: var(--nim-text-13);
86
+ --nim-type-caption-line: 1.76;
87
+
88
+ /* ─── Voice ────────────────────────────────────────────────────────── */
89
+ --nim-press-scale: 0.97;
90
+ --nim-press-translate: 0px;
91
+ }
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />