@engineio/ui 0.4.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 (156) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +274 -0
  3. package/dist/components/brand/engine-icon.svelte +75 -0
  4. package/dist/components/brand/engine-icon.svelte.d.ts +25 -0
  5. package/dist/components/brand/engine-wordmark.svelte +104 -0
  6. package/dist/components/brand/engine-wordmark.svelte.d.ts +16 -0
  7. package/dist/components/brand/index.d.ts +3 -0
  8. package/dist/components/brand/index.js +3 -0
  9. package/dist/components/ui/alert/alert-description.svelte +22 -0
  10. package/dist/components/ui/alert/alert-description.svelte.d.ts +5 -0
  11. package/dist/components/ui/alert/alert-title.svelte +31 -0
  12. package/dist/components/ui/alert/alert-title.svelte.d.ts +8 -0
  13. package/dist/components/ui/alert/alert.svelte +114 -0
  14. package/dist/components/ui/alert/alert.svelte.d.ts +70 -0
  15. package/dist/components/ui/alert/index.d.ts +5 -0
  16. package/dist/components/ui/alert/index.js +7 -0
  17. package/dist/components/ui/badge/badge.svelte +96 -0
  18. package/dist/components/ui/badge/badge.svelte.d.ts +88 -0
  19. package/dist/components/ui/badge/index.d.ts +2 -0
  20. package/dist/components/ui/badge/index.js +2 -0
  21. package/dist/components/ui/button/button.svelte +137 -0
  22. package/dist/components/ui/button/button.svelte.d.ts +154 -0
  23. package/dist/components/ui/button/index.d.ts +2 -0
  24. package/dist/components/ui/button/index.js +4 -0
  25. package/dist/components/ui/card/card-content.svelte +16 -0
  26. package/dist/components/ui/card/card-content.svelte.d.ts +5 -0
  27. package/dist/components/ui/card/card-description.svelte +20 -0
  28. package/dist/components/ui/card/card-description.svelte.d.ts +5 -0
  29. package/dist/components/ui/card/card-footer.svelte +20 -0
  30. package/dist/components/ui/card/card-footer.svelte.d.ts +5 -0
  31. package/dist/components/ui/card/card-header.svelte +20 -0
  32. package/dist/components/ui/card/card-header.svelte.d.ts +5 -0
  33. package/dist/components/ui/card/card-title.svelte +25 -0
  34. package/dist/components/ui/card/card-title.svelte.d.ts +8 -0
  35. package/dist/components/ui/card/card.svelte +23 -0
  36. package/dist/components/ui/card/card.svelte.d.ts +5 -0
  37. package/dist/components/ui/card/index.d.ts +7 -0
  38. package/dist/components/ui/card/index.js +9 -0
  39. package/dist/components/ui/checkbox/checkbox.svelte +38 -0
  40. package/dist/components/ui/checkbox/checkbox.svelte.d.ts +4 -0
  41. package/dist/components/ui/checkbox/index.d.ts +2 -0
  42. package/dist/components/ui/checkbox/index.js +4 -0
  43. package/dist/components/ui/dialog/dialog-content.svelte +41 -0
  44. package/dist/components/ui/dialog/dialog-content.svelte.d.ts +9 -0
  45. package/dist/components/ui/dialog/dialog-description.svelte +16 -0
  46. package/dist/components/ui/dialog/dialog-description.svelte.d.ts +4 -0
  47. package/dist/components/ui/dialog/dialog-footer.svelte +23 -0
  48. package/dist/components/ui/dialog/dialog-footer.svelte.d.ts +5 -0
  49. package/dist/components/ui/dialog/dialog-header.svelte +20 -0
  50. package/dist/components/ui/dialog/dialog-header.svelte.d.ts +5 -0
  51. package/dist/components/ui/dialog/dialog-overlay.svelte +19 -0
  52. package/dist/components/ui/dialog/dialog-overlay.svelte.d.ts +4 -0
  53. package/dist/components/ui/dialog/dialog-title.svelte +16 -0
  54. package/dist/components/ui/dialog/dialog-title.svelte.d.ts +4 -0
  55. package/dist/components/ui/dialog/index.d.ts +12 -0
  56. package/dist/components/ui/dialog/index.js +14 -0
  57. package/dist/components/ui/input/index.d.ts +2 -0
  58. package/dist/components/ui/input/index.js +4 -0
  59. package/dist/components/ui/input/input.svelte +52 -0
  60. package/dist/components/ui/input/input.svelte.d.ts +13 -0
  61. package/dist/components/ui/label/index.d.ts +2 -0
  62. package/dist/components/ui/label/index.js +4 -0
  63. package/dist/components/ui/label/label.svelte +19 -0
  64. package/dist/components/ui/label/label.svelte.d.ts +4 -0
  65. package/dist/components/ui/popover/index.d.ts +6 -0
  66. package/dist/components/ui/popover/index.js +8 -0
  67. package/dist/components/ui/popover/popover-content.svelte +28 -0
  68. package/dist/components/ui/popover/popover-content.svelte.d.ts +7 -0
  69. package/dist/components/ui/progress/index.d.ts +2 -0
  70. package/dist/components/ui/progress/index.js +4 -0
  71. package/dist/components/ui/progress/progress.svelte +25 -0
  72. package/dist/components/ui/progress/progress.svelte.d.ts +4 -0
  73. package/dist/components/ui/radio-group/index.d.ts +3 -0
  74. package/dist/components/ui/radio-group/index.js +5 -0
  75. package/dist/components/ui/radio-group/radio-group-item.svelte +28 -0
  76. package/dist/components/ui/radio-group/radio-group-item.svelte.d.ts +4 -0
  77. package/dist/components/ui/radio-group/radio-group.svelte +13 -0
  78. package/dist/components/ui/radio-group/radio-group.svelte.d.ts +4 -0
  79. package/dist/components/ui/select/index.d.ts +11 -0
  80. package/dist/components/ui/select/index.js +13 -0
  81. package/dist/components/ui/select/select-content.svelte +39 -0
  82. package/dist/components/ui/select/select-content.svelte.d.ts +7 -0
  83. package/dist/components/ui/select/select-group-heading.svelte +16 -0
  84. package/dist/components/ui/select/select-group-heading.svelte.d.ts +4 -0
  85. package/dist/components/ui/select/select-item.svelte +37 -0
  86. package/dist/components/ui/select/select-item.svelte.d.ts +4 -0
  87. package/dist/components/ui/select/select-scroll-down-button.svelte +19 -0
  88. package/dist/components/ui/select/select-scroll-down-button.svelte.d.ts +4 -0
  89. package/dist/components/ui/select/select-scroll-up-button.svelte +19 -0
  90. package/dist/components/ui/select/select-scroll-up-button.svelte.d.ts +4 -0
  91. package/dist/components/ui/select/select-separator.svelte +13 -0
  92. package/dist/components/ui/select/select-separator.svelte.d.ts +4 -0
  93. package/dist/components/ui/select/select-trigger.svelte +24 -0
  94. package/dist/components/ui/select/select-trigger.svelte.d.ts +4 -0
  95. package/dist/components/ui/separator/index.d.ts +2 -0
  96. package/dist/components/ui/separator/index.js +4 -0
  97. package/dist/components/ui/separator/separator.svelte +22 -0
  98. package/dist/components/ui/separator/separator.svelte.d.ts +4 -0
  99. package/dist/components/ui/skeleton/index.d.ts +2 -0
  100. package/dist/components/ui/skeleton/index.js +4 -0
  101. package/dist/components/ui/skeleton/skeleton.svelte +17 -0
  102. package/dist/components/ui/skeleton/skeleton.svelte.d.ts +5 -0
  103. package/dist/components/ui/switch/index.d.ts +2 -0
  104. package/dist/components/ui/switch/index.js +4 -0
  105. package/dist/components/ui/switch/switch.svelte +27 -0
  106. package/dist/components/ui/switch/switch.svelte.d.ts +4 -0
  107. package/dist/components/ui/table/index.d.ts +9 -0
  108. package/dist/components/ui/table/index.js +11 -0
  109. package/dist/components/ui/table/table-body.svelte +22 -0
  110. package/dist/components/ui/table/table-body.svelte.d.ts +5 -0
  111. package/dist/components/ui/table/table-caption.svelte +21 -0
  112. package/dist/components/ui/table/table-caption.svelte.d.ts +5 -0
  113. package/dist/components/ui/table/table-cell.svelte +33 -0
  114. package/dist/components/ui/table/table-cell.svelte.d.ts +5 -0
  115. package/dist/components/ui/table/table-footer.svelte +29 -0
  116. package/dist/components/ui/table/table-footer.svelte.d.ts +5 -0
  117. package/dist/components/ui/table/table-head.svelte +36 -0
  118. package/dist/components/ui/table/table-head.svelte.d.ts +5 -0
  119. package/dist/components/ui/table/table-header.svelte +23 -0
  120. package/dist/components/ui/table/table-header.svelte.d.ts +5 -0
  121. package/dist/components/ui/table/table-row.svelte +32 -0
  122. package/dist/components/ui/table/table-row.svelte.d.ts +5 -0
  123. package/dist/components/ui/table/table.svelte +36 -0
  124. package/dist/components/ui/table/table.svelte.d.ts +5 -0
  125. package/dist/components/ui/tabs/index.d.ts +6 -0
  126. package/dist/components/ui/tabs/index.js +8 -0
  127. package/dist/components/ui/tabs/tabs-content.svelte +19 -0
  128. package/dist/components/ui/tabs/tabs-content.svelte.d.ts +4 -0
  129. package/dist/components/ui/tabs/tabs-list.svelte +27 -0
  130. package/dist/components/ui/tabs/tabs-list.svelte.d.ts +7 -0
  131. package/dist/components/ui/tabs/tabs-trigger.svelte +29 -0
  132. package/dist/components/ui/tabs/tabs-trigger.svelte.d.ts +7 -0
  133. package/dist/components/ui/textarea/index.d.ts +2 -0
  134. package/dist/components/ui/textarea/index.js +4 -0
  135. package/dist/components/ui/textarea/textarea.svelte +22 -0
  136. package/dist/components/ui/textarea/textarea.svelte.d.ts +5 -0
  137. package/dist/components/ui/tooltip/index.d.ts +6 -0
  138. package/dist/components/ui/tooltip/index.js +8 -0
  139. package/dist/components/ui/tooltip/tooltip-content.svelte +21 -0
  140. package/dist/components/ui/tooltip/tooltip-content.svelte.d.ts +4 -0
  141. package/dist/fonts/ProximaNova-ExtraCondensedThin.otf +0 -0
  142. package/dist/fonts/ProximaNovaBlack.otf +0 -0
  143. package/dist/fonts/ProximaNovaBold.otf +0 -0
  144. package/dist/fonts/ProximaNovaCondensedBlack.otf +0 -0
  145. package/dist/fonts/ProximaNovaExtraCondensedBlack.otf +0 -0
  146. package/dist/fonts/ProximaNovaRegular.otf +0 -0
  147. package/dist/fonts/ProximaNovaSemibold.otf +0 -0
  148. package/dist/index.d.ts +45 -0
  149. package/dist/index.js +44 -0
  150. package/dist/styles/fonts.css +116 -0
  151. package/dist/styles/index.css +28 -0
  152. package/dist/styles/tokens.css +221 -0
  153. package/dist/styles/utilities.css +129 -0
  154. package/dist/utils.d.ts +18 -0
  155. package/dist/utils.js +22 -0
  156. package/package.json +92 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Engine
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,274 @@
1
+ # @engineio/ui
2
+
3
+ The Engine design system: tokens, the brand face, and twenty Svelte primitives,
4
+ published once and consumed by every Engine product.
5
+
6
+ Gallery — <https://engineio.github.io/ui>
7
+
8
+ ```bash
9
+ bun add @engineio/ui
10
+ ```
11
+
12
+ Public on npmjs, so that is the whole of it — no registry configuration, no
13
+ token, no `.npmrc`. Published from CI by npm trusted publishing, so every
14
+ version carries provenance linking it back to the commit and workflow run that
15
+ built it.
16
+
17
+ ```css
18
+ /* app.css */
19
+ @import "tailwindcss";
20
+ @import "@engineio/ui/styles";
21
+ @source "../node_modules/@engineio/ui/dist";
22
+ ```
23
+
24
+ ```svelte
25
+ <script>
26
+ import { Button, Card, CardHeader, CardTitle } from "@engineio/ui"
27
+ </script>
28
+
29
+ <Card>
30
+ <CardHeader><CardTitle>Accelerate Growth.</CardTitle></CardHeader>
31
+ <Button>Publish Game</Button>
32
+ </Card>
33
+ ```
34
+
35
+ **That `@source` line is not optional.** Tailwind 4 does not scan
36
+ `node_modules`, so without it the utility classes the primitives are written
37
+ against never get generated. Adjust the relative path to wherever
38
+ `node_modules` sits from the importing stylesheet — it resolves relative to the
39
+ CSS file that declares it.
40
+
41
+ The failure mode is nastier than "nothing renders", which is why it is worth
42
+ getting right first time: the tokens still land (they are plain custom property
43
+ declarations), and any class that *also* appears somewhere in your own source
44
+ still works. So you get a page that is half-styled — magenta fills present,
45
+ `rounded-control` and `border-grey-600` missing — which reads like a component
46
+ bug rather than a build-config one. In a test consumer, omitting the line took
47
+ the stylesheet from 30KB to 6KB with no error at all.
48
+
49
+ ---
50
+
51
+ ## What this is, and what it is not
52
+
53
+ This package owns **brand rules**: colour, type, shape, motion, focus, and the
54
+ twenty primitives that carry them. Change it when the brand changes.
55
+
56
+ It does not own **product behaviour**. Engine Studio's approval queue, rgs's
57
+ operator picker, a `play` button variant — those live in the products. The
58
+ package is deliberately small so it can stay stable.
59
+
60
+ Consumers are **never auto-upgraded**. Cutting a release only makes a version
61
+ available; engine and rgs pick it up when someone bumps the dependency and opens
62
+ a PR. That is the point of publishing rather than sharing source — the upgrade
63
+ is a reviewable diff on the consumer's own schedule.
64
+
65
+ ## Extending a component
66
+
67
+ Every variant map is exported. Add product variants without forking the
68
+ component or waiting on a release here:
69
+
70
+ ```ts
71
+ // $lib/components/ui/button-variants.ts — in YOUR repo
72
+ import { buttonVariants } from "@engineio/ui"
73
+ import { tv } from "tailwind-variants"
74
+
75
+ export const appButtonVariants = tv({
76
+ extend: buttonVariants,
77
+ variants: {
78
+ variant: {
79
+ tab: "bg-transparent text-grey-300 hover:bg-white/6 hover:text-foreground",
80
+ play: "bg-primary text-primary-foreground hover:bg-primary-press",
81
+ },
82
+ },
83
+ })
84
+ ```
85
+
86
+ Brand rules (shape, weight, motion, focus ring) come from the package. Product
87
+ variants stay in the product. If you find yourself wanting to upstream a variant
88
+ that only one product uses, that is the signal to extend instead.
89
+
90
+ `buttonVariants`, `badgeVariants` and `alertVariants` are all exported for
91
+ this. Every primitive also passes `class` through `cn`, so
92
+ `<Button class="w-full" />` works without `!important`.
93
+
94
+ ## What ships
95
+
96
+ **Tokens** — `styles/tokens.css` as a Tailwind 4 `@theme` block. Three brand
97
+ colours, three status colours, a twelve-step neutral ramp, radii by role, two
98
+ shadows, three easings.
99
+
100
+ **The brand face** — Proxima Nova, seven cuts, referenced by relative path so
101
+ your bundler fingerprints them out of `node_modules`. You do not need a copy in
102
+ `static/fonts`.
103
+
104
+ **Marks** — `EngineWordmark` and `EngineIcon`, vector, in the two sanctioned
105
+ colourways (`variant="primary"` white, `variant="secondary"` Off Black). Size
106
+ by height alone. The wordmark is artwork, not type — with the vector in the
107
+ package there is no reason to typeset "engine" in Proxima Nova again.
108
+
109
+ **Primitives** — Alert, Badge, Button, Card, Checkbox, Dialog, Input, Label,
110
+ Popover, Progress, RadioGroup, Select, Separator, Skeleton, Switch, Table, Tabs,
111
+ Textarea, Tooltip.
112
+
113
+ Badge and Tag were merged into one chip. There is no `Tag`: the soft
114
+ repository-label treatment it carried is now what `Badge` looks like, and the
115
+ name `Badge` survived because it is the one in use — 35 call sites in the engine
116
+ repo, against zero for `Tag`.
117
+
118
+ Deliberately excluded: `form`, `data-table`, `drawer`, `resizable`, `carousel`
119
+ and `chart` — heavy dependencies and product-specific APIs. Copy those from the
120
+ engine repo into your product if you need them. `sonner` was excluded because
121
+ toasts need success and error colours that did not exist; now that they do, it
122
+ is a candidate for the next release.
123
+
124
+ ## Development
125
+
126
+ ```bash
127
+ bun install
128
+ bun run dev # the gallery, on :3100
129
+ bun run check # svelte-check
130
+ bun run build # svelte-package -> dist, then publint
131
+ bun run build:gallery # the gallery -> build/
132
+ ```
133
+
134
+ CI enforces what review forgets: no raw hex literals, no Tailwind stock palette,
135
+ no retired sub-brand colours, no emoji. Those four checks exist because all four
136
+ happened in the products.
137
+
138
+ ## Releasing
139
+
140
+ **Every push to `main` releases.** semantic-release reads the conventional
141
+ commits since the last tag, works out the version, tags it, writes the GitHub
142
+ release notes and publishes to GitHub Packages — one run, no release PR, no
143
+ approval step. A push with nothing releasable in it (`docs:`, `chore:`, `ci:`)
144
+ is a no-op rather than a failure.
145
+
146
+ | Commit type | Bump |
147
+ | --- | --- |
148
+ | `feat:`, `tokens:` | minor |
149
+ | `fix:`, `perf:`, `style:`, `refactor:` | patch |
150
+ | `docs:`, `test:`, `build:`, `ci:`, `chore:` | none |
151
+ | any `!` / `BREAKING CHANGE:` | minor, while under 1.0.0 |
152
+
153
+ Use `tokens:` for token changes so they land in their own release-notes
154
+ section.
155
+
156
+ **Two things are deliberately not tracked in git.** `package.json`'s version is
157
+ the placeholder `0.0.0-development` — tags are the source of truth, and
158
+ semantic-release writes the real version in the runner just before publishing.
159
+ And there is no `CHANGELOG.md`; the release notes are the changelog, generated
160
+ from the same commits. Both follow from `main` requiring pull requests: the
161
+ usual setup commits the bump and changelog back to the release branch, which
162
+ would need a bypass no CI job should hold.
163
+
164
+ We are on `0.x` on purpose. Semver means something the moment two repos depend
165
+ on you, and the token layer is still moving — breaking changes are cheap and
166
+ expected until it settles. That is enforced by `breaking → minor` in
167
+ `.releaserc.json`; deleting that rule is the deliberate act that cuts `1.0.0`
168
+ and starts the promises.
169
+
170
+ ## Known gaps
171
+
172
+ Stated rather than designed around. Most are inherited from the brand; the
173
+ first is a decision made here that the brand documents have not caught up with.
174
+
175
+ 1. **The status palette is a local decision the brand documents do not yet
176
+ know about.** Success `#00C46A`, warning `#FFB020` and danger `#FF3B30` now
177
+ exist in `tokens.css`, commissioned at the repo owner's direction to close
178
+ what was previously this list's worst gap. `--color-destructive` aliases
179
+ danger, so Alert, the Button's destructive variant and Input's
180
+ `aria-invalid` state all resolve to a real red instead of to magenta.
181
+
182
+ Every value clears 4.5:1 three ways — Off Black as ink on the fill, and the
183
+ colour as text on both the page and its own 12% tint. **Ink on a status
184
+ fill is always Off Black**; white fails on all three.
185
+
186
+ The outstanding work is documentary, not technical: `ENGINE-BRAND.md` §10
187
+ and `ENGINE-DESIGN-SYSTEM.md` §12 in the engine repo still state that no
188
+ status palette exists and that one must not be invented. Those sections
189
+ need updating and the values want brand-team ratification. Until that
190
+ happens, this package and the brand documents disagree — and by this repo's
191
+ own rule (see Source of truth) the documents win, so treat these three as
192
+ provisional.
193
+
194
+ One judgement call inside the palette: warning sits ΔE 28.7 from Partner
195
+ Yellow `#FFDD00` — visibly amber beside that pure yellow, but not
196
+ dramatically. It does not matter while Partner Yellow is unused; if Engine
197
+ Integration adopts it, a warning chip next to a Partner badge on one surface
198
+ wants another look. And there is deliberately **no `info` colour**: the
199
+ obvious choice is a blue, and any blue would read as the retired Sportsbook
200
+ Blue returning. Use the neutral ramp for informational states.
201
+
202
+ 2. **Still no categorical palette.** `Badge` ships the soft repository-label
203
+ treatment — tinted fill, matching ink — in eight variants, four of which are
204
+ the functional status colours. A *categorical* palette (one hue per topic,
205
+ twenty of them) is a separate and still-unmade decision: eighteen accents on
206
+ one surface is the direct opposite of "one accent per surface, never two".
207
+ Do not press the status colours into that job — using `success` to mean
208
+ "slots" because green looked right spends the only signal the palette
209
+ carries. If a categorical palette is granted, declare it here once as a
210
+ named group rather than as one-off fills discovered in a diff later.
211
+
212
+ 3. **Two deliberate deviations from the brand document**, both at the repo
213
+ owner's direction, both leaving the code and the document in disagreement.
214
+
215
+ **Badge is one component where §6 specifies two.** The document defines
216
+ Badge and Tag separately, Badge as a filled pill with white ink. This
217
+ package ships a single soft chip, because the two-component rule only ever
218
+ generated arguments about which a given label was. It does resolve a
219
+ contradiction that used to sit in this list: the filled badge's
220
+ white-on-magenta at 11px measured 3.9:1, a documented AA failure, against
221
+ 5.81:1 for the soft chip's `primary-300` ink on a magenta tint.
222
+
223
+ **Alert signals state with a coloured left-edge bar**, which the brand
224
+ prohibits — twice. Worth reading the wording precisely, because it is
225
+ narrower than it first looks: ENGINE-BRAND.md §2 bans "a coloured left-edge
226
+ accent **to signal which sub-brand a card belongs to**", and
227
+ ENGINE-DESIGN-SYSTEM.md §11 bans "a coloured left border **to indicate
228
+ category or ownership**". Both scope the prohibition to signalling
229
+ identity, and both give the same reason: the lockup does that job. A status
230
+ bar signals state, which no lockup can express. That is the reading this is
231
+ built on — a reading, not an exemption.
232
+
233
+ Either way, §6 and §11 need updating or these need reverting, and by this
234
+ repo's own source-of-truth rule the document nominally wins.
235
+
236
+ 4. **JetBrains Mono is not loaded.** The brand specifies it for all numerics,
237
+ keys, IDs, code and eyebrow labels. Neither engine nor rgs has ever shipped
238
+ the binary, so every monospace surface in both products renders in the
239
+ platform default. `--font-mono` falls back safely, but the brand face is
240
+ absent. It is OFL-licensed and free to redistribute: drop the `.woff2` files
241
+ into `src/lib/fonts/` and add the `@font-face` blocks to `styles/fonts.css`.
242
+
243
+ 5. **Proxima Nova is commercially licensed.** The seven cuts ship inside this
244
+ package, which is private and org-scoped — the same internal distribution as
245
+ the copies already sitting in engine's and rgs's `static/fonts`. Worth a
246
+ glance from whoever owns the licence before this goes anywhere less private.
247
+ Inter is the documented substitute where the licence does not reach.
248
+
249
+ 6. **Engine Integration's accent is unsettled.** Brand direction assigns it
250
+ Partner Yellow; every shipped surface is magenta and was deliberately
251
+ migrated there. `--color-partner-yellow` is declared and available; build
252
+ Integration in magenta until the call is made. A half-migrated accent is
253
+ worse than a consistent one.
254
+
255
+ 7. **No vector sub-brand artwork.** The wordmark and the figure mark are
256
+ vector and ship here. The 3x3 dot badges that precede a sub-brand name in a
257
+ lockup are raster extractions at roughly 29px and go soft above 24px, so
258
+ this package ships no `SubBrandLockup` at all. The originals are a
259
+ brand-team ask.
260
+
261
+ 8. **Layout and scrim tokens live only in the document.** `--space-*`,
262
+ `--page-max`, `--content-max` and `--scrim-bottom` are specified in the
263
+ design system document §2 and are not declared in `tokens.css`. Tailwind's
264
+ own 4px scale already produces every spacing step (`p-14` is 56px, `p-32`
265
+ is 128px), and the two page widths are carried as arbitrary values, so the
266
+ numbers are reachable but unnamed. The gallery's imagery section sets the
267
+ scrim inline for the same reason.
268
+
269
+ ## Source of truth
270
+
271
+ `docs/brand/ENGINE-DESIGN-SYSTEM.md` in the engine repo is the written
272
+ specification, and `docs/brand/ENGINE-BRAND.md` the brand guidelines. Where this
273
+ package and those documents disagree, **the documents win and this package is
274
+ wrong** — open an issue rather than changing the document to match the code.
@@ -0,0 +1,75 @@
1
+ <script lang="ts" module>
2
+ /**
3
+ * The two sanctioned colourways, and the only two.
4
+ *
5
+ * primary — Pure White, for the dark surfaces the brand actually uses
6
+ * secondary — Off Black, for print, letterhead and inverted brand panels
7
+ *
8
+ * These are named after the source artwork (Engine_Primary_Icon.svg /
9
+ * Engine_Secondary_Icon.svg) rather than after the colour, so the files and
10
+ * the code agree.
11
+ */
12
+ export type EngineMarkVariant = "primary" | "secondary"
13
+ </script>
14
+
15
+ <script lang="ts">
16
+ import { cn } from "../../utils.js"
17
+ import type { SVGAttributes } from "svelte/elements"
18
+
19
+ // The Engine figure mark. Vector, from the brand team's own artwork — which
20
+ // closes the gap the earlier marks had: every previous badge and lockup in
21
+ // this system was a raster extraction from deck page images at roughly 29px,
22
+ // and went soft above 24px. This scales.
23
+ //
24
+ // THE MARK IS ARTWORK, NOT AN ICON. The brand allows it in white or Off
25
+ // Black and nothing else — never outlined, stretched, skewed, or recoloured.
26
+ // That is why colour is a two-value `variant` rather than `currentColor`:
27
+ // with currentColor any ancestor's text colour silently becomes brand
28
+ // colour, and a magenta wordmark is one careless `text-primary` away. The
29
+ // variant makes the sanctioned choice the only reachable one.
30
+ //
31
+ // Sizing is by class, and height-only: `class="h-8"` with width following
32
+ // from the viewBox. Never set both — the brand forbids distortion, and the
33
+ // 810x1000 ratio is not one anybody reproduces by hand.
34
+
35
+ let {
36
+ ref = $bindable(null),
37
+ variant = "primary",
38
+ class: className,
39
+ title,
40
+ ...restProps
41
+ }: SVGAttributes<SVGSVGElement> & {
42
+ ref?: SVGSVGElement | null
43
+ variant?: EngineMarkVariant
44
+ /**
45
+ * Accessible name. Omit when the mark sits beside the word "Engine" or
46
+ * other naming text — then it is decorative and gets aria-hidden, so a
47
+ * screen reader does not announce the brand twice.
48
+ */
49
+ title?: string
50
+ } = $props()
51
+ </script>
52
+
53
+ <svg
54
+ bind:this={ref}
55
+ viewBox="0 0 810 1000"
56
+ xmlns="http://www.w3.org/2000/svg"
57
+ class={cn(
58
+ "h-8 w-auto shrink-0",
59
+ variant === "primary" ? "text-foreground" : "text-background",
60
+ className,
61
+ )}
62
+ role={title ? "img" : undefined}
63
+ aria-hidden={title ? undefined : "true"}
64
+ {...restProps}
65
+ >
66
+ {#if title}
67
+ <title>{title}</title>
68
+ {/if}
69
+ <path
70
+ fill="currentColor"
71
+ fill-rule="evenodd"
72
+ clip-rule="evenodd"
73
+ d="M809.525 207.792H755.007C786.985 265.518 805.192 331.931 805.192 402.597C805.192 521.09 754 627.625 672.527 701.297C745.977 767.713 794.816 860.839 803.724 965.356C805.349 984.429 789.676 1000 770.534 1000H632.061C612.919 1000 597.724 984.35 594.357 965.507C578.074 874.372 498.414 805.195 402.597 805.195C306.78 805.195 227.121 874.372 210.837 965.507C207.47 984.35 192.275 1000 173.134 1000H34.6592C15.5177 1000 -0.154779 984.429 1.4707 965.356C10.3784 860.84 59.2162 767.714 132.665 701.298C51.192 627.626 3.5177e-05 521.09 0 402.597C0 180.249 180.249 0 402.597 0H809.525V207.792ZM402.597 207.793C295.009 207.793 207.792 295.01 207.792 402.598C207.792 510.185 295.009 597.402 402.597 597.402C510.184 597.402 597.401 510.185 597.401 402.598C597.401 295.01 510.184 207.793 402.597 207.793Z"
74
+ />
75
+ </svg>
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The two sanctioned colourways, and the only two.
3
+ *
4
+ * primary — Pure White, for the dark surfaces the brand actually uses
5
+ * secondary — Off Black, for print, letterhead and inverted brand panels
6
+ *
7
+ * These are named after the source artwork (Engine_Primary_Icon.svg /
8
+ * Engine_Secondary_Icon.svg) rather than after the colour, so the files and
9
+ * the code agree.
10
+ */
11
+ export type EngineMarkVariant = "primary" | "secondary";
12
+ import type { SVGAttributes } from "svelte/elements";
13
+ type $$ComponentProps = SVGAttributes<SVGSVGElement> & {
14
+ ref?: SVGSVGElement | null;
15
+ variant?: EngineMarkVariant;
16
+ /**
17
+ * Accessible name. Omit when the mark sits beside the word "Engine" or
18
+ * other naming text — then it is decorative and gets aria-hidden, so a
19
+ * screen reader does not announce the brand twice.
20
+ */
21
+ title?: string;
22
+ };
23
+ declare const EngineIcon: import("svelte").Component<$$ComponentProps, {}, "ref">;
24
+ type EngineIcon = ReturnType<typeof EngineIcon>;
25
+ export default EngineIcon;
@@ -0,0 +1,104 @@
1
+ <script lang="ts">
2
+ import { cn } from "../../utils.js"
3
+ import type { SVGAttributes } from "svelte/elements"
4
+ import type { EngineMarkVariant } from "./engine-icon.svelte"
5
+
6
+ // The Engine wordmark. Vector, from the brand team's artwork.
7
+ //
8
+ // "The engine wordmark is a custom soft-rounded face, not Proxima Nova. It is
9
+ // artwork. Never set the wordmark in type, never approximate it in a
10
+ // lookalike, never outline, stretch, skew or recolour it beyond white / Off
11
+ // Black." That rule is the reason this component exists. With the vector in
12
+ // the package there is no longer any excuse to typeset "engine" in Proxima
13
+ // Nova and hope.
14
+ //
15
+ // Colour is the same two-value variant as EngineIcon, for the same reason:
16
+ // `currentColor` would let any ancestor's text colour become brand colour.
17
+ //
18
+ // THE viewBox IS NOT THE ARTWORK'S BOUNDING BOX, AND MUST NOT BE "FIXED".
19
+ //
20
+ // The paths occupy 0..449 vertically: the letters sit on a baseline at
21
+ // 361.532 and the g descends 87.468 below it. That box centres on 224.5,
22
+ // while the word itself centres on 180.766. Anything vertically centring
23
+ // this mark, such as a flex header or a table cell, put the word 43.734
24
+ // units too high and left the descender doing the balancing. At h-7 that is
25
+ // most of 3px, which is plainly visible beside adjacent text.
26
+ //
27
+ // So the box is padded above the letters by exactly the descender depth,
28
+ // 87.468. The artwork is untouched; the box is now symmetric about the word,
29
+ // its centre (180.766) lands on the word's centre, and `items-center` is
30
+ // correct with no per-call-site nudging.
31
+ //
32
+ // The cost is that height now buys 536.468 units instead of 449, so the
33
+ // letters render ~16% smaller for a given `h-*` than they did before. That
34
+ // is the trade for a box that centres: one step up the scale restores it.
35
+ //
36
+ // Sizing is height-only by class. At 2000x536.468, `class="h-6"` gives a
37
+ // ~89px-wide wordmark. Check the small end, because the counters in the two
38
+ // "e"s and the "g" close up before the shape stops being legible.
39
+ // Master-brand surfaces carry this; a sub-brand surface carries a lockup
40
+ // (wordmark plus the sub-brand name), which this package does not ship
41
+ // because the badge artwork is still raster.
42
+
43
+ let {
44
+ ref = $bindable(null),
45
+ variant = "primary",
46
+ class: className,
47
+ title = "Engine",
48
+ ...restProps
49
+ }: SVGAttributes<SVGSVGElement> & {
50
+ ref?: SVGSVGElement | null
51
+ variant?: EngineMarkVariant
52
+ /**
53
+ * Accessible name, defaulting to "Engine". Unlike the icon, a wordmark
54
+ * carries the company name as its content, so it is almost never
55
+ * decorative. Pass `title={undefined}` to hide it from screen readers when
56
+ * it sits next to naming text.
57
+ */
58
+ title?: string | undefined
59
+ } = $props()
60
+ </script>
61
+
62
+ <svg
63
+ bind:this={ref}
64
+ viewBox="0 -87.468 2000 536.468"
65
+ xmlns="http://www.w3.org/2000/svg"
66
+ class={cn(
67
+ "h-6 w-auto shrink-0",
68
+ variant === "primary" ? "text-foreground" : "text-background",
69
+ className,
70
+ )}
71
+ role={title ? "img" : undefined}
72
+ aria-hidden={title ? undefined : "true"}
73
+ {...restProps}
74
+ >
75
+ {#if title}
76
+ <title>{title}</title>
77
+ {/if}
78
+ <g fill="currentColor">
79
+ <path
80
+ fill-rule="evenodd"
81
+ clip-rule="evenodd"
82
+ d="M1124.28 93.2987H1099.82C1114.16 119.218 1122.33 149.036 1122.33 180.765C1122.33 233.968 1099.36 281.802 1062.82 314.881C1095.77 344.704 1117.68 386.522 1121.67 433.456C1122.4 442.013 1115.38 448.999 1106.8 449H1044.66C1036.08 449 1029.27 441.978 1027.76 433.524C1020.46 392.599 984.73 361.532 941.748 361.532C898.765 361.532 863.032 392.599 855.733 433.524C854.225 441.978 847.414 449 838.835 449H776.699C768.119 449 761.096 442.013 761.823 433.456C765.816 386.523 787.723 344.704 820.671 314.881C784.127 281.802 761.163 233.968 761.163 180.765C761.164 80.9315 842.013 0.000368566 941.745 0H1124.28V93.2987ZM941.748 93.2987C893.491 93.2994 854.371 132.459 854.37 180.765C854.37 229.071 893.49 268.232 941.748 268.233C990.005 268.233 1029.13 229.071 1029.13 180.765C1029.13 132.459 990.005 93.2987 941.748 93.2987Z"
83
+ />
84
+ <path
85
+ d="M557.281 3.88776C656.366 3.88775 736.812 83.771 737.853 182.71H737.865V345.983C737.865 354.57 730.909 361.532 722.33 361.532H660.194C651.615 361.531 644.661 354.57 644.661 345.983V184.652L644.631 182.395C643.435 135.132 604.785 97.1865 557.281 97.1865C509.777 97.1869 471.127 135.132 469.931 182.395L469.903 184.652V345.983C469.903 354.57 462.949 361.531 454.371 361.532H392.234C383.656 361.532 376.7 354.57 376.699 345.983V182.71H376.711C377.753 83.7712 458.196 3.88812 557.281 3.88776Z"
86
+ />
87
+ <path
88
+ d="M1225.24 0C1233.82 0.00063118 1240.78 6.9616 1240.78 15.5485V345.981C1240.78 354.568 1233.82 361.531 1225.24 361.532H1163.11C1154.53 361.531 1147.57 354.567 1147.57 345.978V15.551C1147.57 6.96271 1154.53 0.00051524 1163.11 0H1225.24Z"
89
+ />
90
+ <path
91
+ d="M1444.66 3.88776C1543.75 3.88775 1624.19 83.7707 1625.23 182.71H1625.24V345.983C1625.24 354.57 1618.29 361.532 1609.71 361.532H1547.58C1539 361.532 1532.04 354.57 1532.04 345.983V184.652L1532.01 182.395C1530.81 135.132 1492.16 97.1865 1444.66 97.1865C1397.16 97.1869 1358.51 135.132 1357.31 182.395L1357.28 184.652V345.983C1357.28 354.57 1350.33 361.531 1341.75 361.532H1279.61C1271.03 361.532 1264.08 354.57 1264.08 345.983V182.71H1264.09C1265.13 83.7709 1345.57 3.88812 1444.66 3.88776Z"
92
+ />
93
+ <path
94
+ fill-rule="evenodd"
95
+ clip-rule="evenodd"
96
+ d="M180.582 3.88776C279.667 3.88775 360.113 83.771 361.154 182.71H361.166V198.258C361.166 206.846 354.212 213.809 345.633 213.809H100.481C113.966 244.699 144.753 266.288 180.582 266.288C201.923 266.288 221.478 258.63 236.652 245.909C242.288 241.184 250.272 239.768 256.639 243.448L312.403 275.674C320.364 280.275 322.649 290.798 316.601 297.727C283.498 335.638 234.834 359.587 180.582 359.587C82.7983 359.586 3.17237 281.787 0.097158 184.652H0C3.50508e-06 183.679 0.00958911 182.707 0.0249123 181.737C0.00958911 180.767 3.50462e-06 179.796 0 178.822H0.097158C3.17237 81.687 82.7983 3.88812 180.582 3.88776ZM180.582 97.1865C147.053 97.1867 117.94 116.093 103.289 143.835H257.875C243.224 116.093 214.111 97.1865 180.582 97.1865Z"
97
+ />
98
+ <path
99
+ fill-rule="evenodd"
100
+ clip-rule="evenodd"
101
+ d="M1819.42 3.88776C1918.5 3.88775 1998.95 83.771 1999.99 182.71H2000V198.258C2000 206.846 1993.05 213.809 1984.47 213.809H1739.32C1752.8 244.699 1783.59 266.288 1819.42 266.288C1840.76 266.288 1860.31 258.63 1875.49 245.909C1881.12 241.184 1889.11 239.768 1895.47 243.448L1951.24 275.674C1959.2 280.275 1961.48 290.798 1955.43 297.727C1922.33 335.638 1873.67 359.587 1819.42 359.587C1721.63 359.586 1642.01 281.787 1638.93 184.652H1638.83C1638.83 183.679 1638.84 182.707 1638.86 181.737C1638.84 180.767 1638.83 179.796 1638.83 178.822H1638.93C1642.01 81.687 1721.63 3.88812 1819.42 3.88776ZM1819.42 97.1865C1785.89 97.1867 1756.77 116.093 1742.12 143.835H1896.71C1882.06 116.093 1852.94 97.1865 1819.42 97.1865Z"
102
+ />
103
+ </g>
104
+ </svg>
@@ -0,0 +1,16 @@
1
+ import type { SVGAttributes } from "svelte/elements";
2
+ import type { EngineMarkVariant } from "./engine-icon.svelte";
3
+ type $$ComponentProps = SVGAttributes<SVGSVGElement> & {
4
+ ref?: SVGSVGElement | null;
5
+ variant?: EngineMarkVariant;
6
+ /**
7
+ * Accessible name, defaulting to "Engine". Unlike the icon, a wordmark
8
+ * carries the company name as its content, so it is almost never
9
+ * decorative. Pass `title={undefined}` to hide it from screen readers when
10
+ * it sits next to naming text.
11
+ */
12
+ title?: string | undefined;
13
+ };
14
+ declare const EngineWordmark: import("svelte").Component<$$ComponentProps, {}, "ref">;
15
+ type EngineWordmark = ReturnType<typeof EngineWordmark>;
16
+ export default EngineWordmark;
@@ -0,0 +1,3 @@
1
+ import EngineIcon, { type EngineMarkVariant } from "./engine-icon.svelte";
2
+ import EngineWordmark from "./engine-wordmark.svelte";
3
+ export { EngineIcon, EngineWordmark, type EngineMarkVariant };
@@ -0,0 +1,3 @@
1
+ import EngineIcon, {} from "./engine-icon.svelte";
2
+ import EngineWordmark from "./engine-wordmark.svelte";
3
+ export { EngineIcon, EngineWordmark };
@@ -0,0 +1,22 @@
1
+ <script lang="ts">
2
+ import type { WithElementRef } from "../../../utils.js"
3
+ import { cn } from "../../../utils.js"
4
+ import type { HTMLAttributes } from "svelte/elements"
5
+
6
+ // 13px on the grey ramp, one step under the title. Never tinted to match the
7
+ // bar — the panel stays neutral so the bar is the only signal.
8
+ let {
9
+ ref = $bindable(null),
10
+ class: className,
11
+ children,
12
+ ...restProps
13
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props()
14
+ </script>
15
+
16
+ <div
17
+ bind:this={ref}
18
+ class={cn("mt-1 text-[13px] leading-[1.45] text-grey-300", className)}
19
+ {...restProps}
20
+ >
21
+ {@render children?.()}
22
+ </div>
@@ -0,0 +1,5 @@
1
+ import type { WithElementRef } from "../../../utils.js";
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ declare const AlertDescription: import("svelte").Component<WithElementRef<HTMLAttributes<HTMLDivElement>>, {}, "ref">;
4
+ type AlertDescription = ReturnType<typeof AlertDescription>;
5
+ export default AlertDescription;
@@ -0,0 +1,31 @@
1
+ <script lang="ts">
2
+ import type { WithElementRef } from "../../../utils.js"
3
+ import { cn } from "../../../utils.js"
4
+ import type { HTMLAttributes } from "svelte/elements"
5
+
6
+ // Bold 15px Pure White. The title carries the message — the bar only says
7
+ // which kind of message it is, so the words have to work with the colour
8
+ // ignored entirely. "Upload failed." not "Error."
9
+ let {
10
+ ref = $bindable(null),
11
+ class: className,
12
+ level = 5,
13
+ children,
14
+ ...restProps
15
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
16
+ level?: 1 | 2 | 3 | 4 | 5 | 6
17
+ } = $props()
18
+ </script>
19
+
20
+ <div
21
+ role="heading"
22
+ aria-level={level}
23
+ bind:this={ref}
24
+ class={cn(
25
+ "text-[15px] leading-[1.2] font-bold tracking-[-0.01em] text-foreground",
26
+ className,
27
+ )}
28
+ {...restProps}
29
+ >
30
+ {@render children?.()}
31
+ </div>
@@ -0,0 +1,8 @@
1
+ import type { WithElementRef } from "../../../utils.js";
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ type $$ComponentProps = WithElementRef<HTMLAttributes<HTMLDivElement>> & {
4
+ level?: 1 | 2 | 3 | 4 | 5 | 6;
5
+ };
6
+ declare const AlertTitle: import("svelte").Component<$$ComponentProps, {}, "ref">;
7
+ type AlertTitle = ReturnType<typeof AlertTitle>;
8
+ export default AlertTitle;