@crewlethq/icons 0.2.0 → 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.
- package/README.md +131 -15
- package/TRADEMARKS.md +13 -0
- package/dist/CrewletFigure-4PMMCAFG.css +85 -0
- package/dist/Glyph-kbRsfe3S.d.ts +57 -0
- package/dist/VendorMark-AGZRLMVT.css +32 -0
- package/dist/chunk-CUOUFELK.js +49 -0
- package/dist/chunk-IBPOFE7X.js +740 -0
- package/dist/glyphs-registry.d.ts +23 -0
- package/dist/glyphs-registry.js +118 -0
- package/dist/glyphs.d.ts +217 -0
- package/dist/glyphs.js +7 -0
- package/dist/index-DRMc9hb_.d.ts +4 -0
- package/dist/index.d.ts +32 -23
- package/dist/index.js +200 -125
- package/favicon/crewlet-admin.svg +10 -0
- package/favicon/crewlet.ico +0 -0
- package/favicon/crewlet.svg +11 -0
- package/package.json +24 -6
- package/svg/code-sandbox.svg +19 -0
- package/svg/company-as-code.svg +1 -1
- package/svg/hierarchy.svg +1 -1
- package/svg/human-in-loop.svg +1 -1
- package/svg/knowledge.svg +18 -0
- package/svg/turn-engine.svg +19 -0
- package/svg/vendor/atlassian.svg +10 -0
- package/svg/vendor/clickup.svg +3 -0
- package/svg/vendor/datadog.svg +4 -0
- package/svg/vendor/discord.svg +3 -0
- package/svg/vendor/figma.svg +7 -0
- package/svg/vendor/github.svg +3 -0
- package/svg/vendor/gitlab.svg +6 -0
- package/svg/vendor/mattermost.svg +3 -0
- package/svg/vendor/microsoft-teams.svg +3 -0
- package/svg/vendor/notion.svg +3 -0
- package/svg/vendor/slack.svg +6 -0
- package/svg/vendor/telegram.svg +3 -0
- package/symbols/20/account_tree.svg +1 -0
- package/symbols/20/add.svg +1 -0
- package/symbols/20/apartment.svg +1 -0
- package/symbols/20/arrow_downward.svg +1 -0
- package/symbols/20/arrow_forward.svg +1 -0
- package/symbols/20/arrow_outward.svg +1 -0
- package/symbols/20/arrow_upward.svg +1 -0
- package/symbols/20/autorenew.svg +1 -0
- package/symbols/20/block.svg +1 -0
- package/symbols/20/bolt.svg +1 -0
- package/symbols/20/book_2.svg +1 -0
- package/symbols/20/bug_report.svg +1 -0
- package/symbols/20/build.svg +1 -0
- package/symbols/20/cable.svg +1 -0
- package/symbols/20/cached.svg +1 -0
- package/symbols/20/calendar_clock.svg +1 -0
- package/symbols/20/calendar_today.svg +1 -0
- package/symbols/20/chat.svg +1 -0
- package/symbols/20/check.svg +1 -0
- package/symbols/20/check_circle-fill.svg +1 -0
- package/symbols/20/check_circle.svg +1 -0
- package/symbols/20/chevron_left.svg +1 -0
- package/symbols/20/chevron_right.svg +1 -0
- package/symbols/20/close.svg +1 -0
- package/symbols/20/code.svg +1 -0
- package/symbols/20/computer.svg +1 -0
- package/symbols/20/content_copy.svg +1 -0
- package/symbols/20/create_new_folder.svg +1 -0
- package/symbols/20/crown.svg +1 -0
- package/symbols/20/cycle.svg +1 -0
- package/symbols/20/dark_mode.svg +1 -0
- package/symbols/20/dashboard.svg +1 -0
- package/symbols/20/database.svg +1 -0
- package/symbols/20/delete.svg +1 -0
- package/symbols/20/description.svg +1 -0
- package/symbols/20/difference.svg +1 -0
- package/symbols/20/dns.svg +1 -0
- package/symbols/20/drag_indicator.svg +1 -0
- package/symbols/20/edit.svg +1 -0
- package/symbols/20/error-fill.svg +1 -0
- package/symbols/20/error.svg +1 -0
- package/symbols/20/explore.svg +1 -0
- package/symbols/20/fit_screen.svg +1 -0
- package/symbols/20/flag.svg +1 -0
- package/symbols/20/folder.svg +1 -0
- package/symbols/20/fork_right.svg +1 -0
- package/symbols/20/fullscreen.svg +1 -0
- package/symbols/20/fullscreen_exit.svg +1 -0
- package/symbols/20/group.svg +1 -0
- package/symbols/20/help.svg +1 -0
- package/symbols/20/inbox.svg +1 -0
- package/symbols/20/info-fill.svg +1 -0
- package/symbols/20/info.svg +1 -0
- package/symbols/20/key.svg +1 -0
- package/symbols/20/keyboard_arrow_down.svg +1 -0
- package/symbols/20/keyboard_arrow_up.svg +1 -0
- package/symbols/20/keyboard_double_arrow_left.svg +1 -0
- package/symbols/20/keyboard_double_arrow_right.svg +1 -0
- package/symbols/20/layers.svg +1 -0
- package/symbols/20/light_mode.svg +1 -0
- package/symbols/20/link.svg +1 -0
- package/symbols/20/list.svg +1 -0
- package/symbols/20/manufacturing.svg +1 -0
- package/symbols/20/memory.svg +1 -0
- package/symbols/20/menu.svg +1 -0
- package/symbols/20/more_vert.svg +1 -0
- package/symbols/20/move_item.svg +1 -0
- package/symbols/20/neurology.svg +1 -0
- package/symbols/20/notifications.svg +1 -0
- package/symbols/20/open_in_new.svg +1 -0
- package/symbols/20/package_2.svg +1 -0
- package/symbols/20/pause.svg +1 -0
- package/symbols/20/person.svg +1 -0
- package/symbols/20/person_add.svg +1 -0
- package/symbols/20/photo_camera.svg +1 -0
- package/symbols/20/power_settings_new.svg +1 -0
- package/symbols/20/redo.svg +1 -0
- package/symbols/20/refresh.svg +1 -0
- package/symbols/20/remove.svg +1 -0
- package/symbols/20/save.svg +1 -0
- package/symbols/20/schedule.svg +1 -0
- package/symbols/20/search.svg +1 -0
- package/symbols/20/settings.svg +1 -0
- package/symbols/20/settings_backup_restore.svg +1 -0
- package/symbols/20/shield.svg +1 -0
- package/symbols/20/shield_person.svg +1 -0
- package/symbols/20/smart_toy.svg +1 -0
- package/symbols/20/star-fill.svg +1 -0
- package/symbols/20/star.svg +1 -0
- package/symbols/20/swap_vert.svg +1 -0
- package/symbols/20/tag.svg +1 -0
- package/symbols/20/target.svg +1 -0
- package/symbols/20/terminal.svg +1 -0
- package/symbols/20/timeline.svg +1 -0
- package/symbols/20/token.svg +1 -0
- package/symbols/20/tune.svg +1 -0
- package/symbols/20/undo.svg +1 -0
- package/symbols/20/unfold_more.svg +1 -0
- package/symbols/20/view_column.svg +1 -0
- package/symbols/20/visibility.svg +1 -0
- package/symbols/20/visibility_off.svg +1 -0
- package/symbols/20/warning-fill.svg +1 -0
- package/symbols/20/warning.svg +1 -0
- package/symbols/20/zoom_in.svg +1 -0
- package/symbols/20/zoom_out.svg +1 -0
- package/symbols/24/account_tree.svg +1 -0
- package/symbols/24/add.svg +1 -0
- package/symbols/24/apartment.svg +1 -0
- package/symbols/24/arrow_downward.svg +1 -0
- package/symbols/24/arrow_forward.svg +1 -0
- package/symbols/24/arrow_outward.svg +1 -0
- package/symbols/24/arrow_upward.svg +1 -0
- package/symbols/24/autorenew.svg +1 -0
- package/symbols/24/block.svg +1 -0
- package/symbols/24/bolt.svg +1 -0
- package/symbols/24/book_2.svg +1 -0
- package/symbols/24/bug_report.svg +1 -0
- package/symbols/24/build.svg +1 -0
- package/symbols/24/cable.svg +1 -0
- package/symbols/24/cached.svg +1 -0
- package/symbols/24/calendar_clock.svg +1 -0
- package/symbols/24/calendar_today.svg +1 -0
- package/symbols/24/chat.svg +1 -0
- package/symbols/24/check.svg +1 -0
- package/symbols/24/check_circle-fill.svg +1 -0
- package/symbols/24/check_circle.svg +1 -0
- package/symbols/24/chevron_left.svg +1 -0
- package/symbols/24/chevron_right.svg +1 -0
- package/symbols/24/close.svg +1 -0
- package/symbols/24/code.svg +1 -0
- package/symbols/24/computer.svg +1 -0
- package/symbols/24/content_copy.svg +1 -0
- package/symbols/24/create_new_folder.svg +1 -0
- package/symbols/24/crown.svg +1 -0
- package/symbols/24/cycle.svg +1 -0
- package/symbols/24/dark_mode.svg +1 -0
- package/symbols/24/dashboard.svg +1 -0
- package/symbols/24/database.svg +1 -0
- package/symbols/24/delete.svg +1 -0
- package/symbols/24/description.svg +1 -0
- package/symbols/24/difference.svg +1 -0
- package/symbols/24/dns.svg +1 -0
- package/symbols/24/drag_indicator.svg +1 -0
- package/symbols/24/edit.svg +1 -0
- package/symbols/24/error-fill.svg +1 -0
- package/symbols/24/error.svg +1 -0
- package/symbols/24/explore.svg +1 -0
- package/symbols/24/fit_screen.svg +1 -0
- package/symbols/24/flag.svg +1 -0
- package/symbols/24/folder.svg +1 -0
- package/symbols/24/fork_right.svg +1 -0
- package/symbols/24/fullscreen.svg +1 -0
- package/symbols/24/fullscreen_exit.svg +1 -0
- package/symbols/24/group.svg +1 -0
- package/symbols/24/help.svg +1 -0
- package/symbols/24/inbox.svg +1 -0
- package/symbols/24/info-fill.svg +1 -0
- package/symbols/24/info.svg +1 -0
- package/symbols/24/key.svg +1 -0
- package/symbols/24/keyboard_arrow_down.svg +1 -0
- package/symbols/24/keyboard_arrow_up.svg +1 -0
- package/symbols/24/keyboard_double_arrow_left.svg +1 -0
- package/symbols/24/keyboard_double_arrow_right.svg +1 -0
- package/symbols/24/layers.svg +1 -0
- package/symbols/24/light_mode.svg +1 -0
- package/symbols/24/link.svg +1 -0
- package/symbols/24/list.svg +1 -0
- package/symbols/24/manufacturing.svg +1 -0
- package/symbols/24/memory.svg +1 -0
- package/symbols/24/menu.svg +1 -0
- package/symbols/24/more_vert.svg +1 -0
- package/symbols/24/move_item.svg +1 -0
- package/symbols/24/neurology.svg +1 -0
- package/symbols/24/notifications.svg +1 -0
- package/symbols/24/open_in_new.svg +1 -0
- package/symbols/24/package_2.svg +1 -0
- package/symbols/24/pause.svg +1 -0
- package/symbols/24/person.svg +1 -0
- package/symbols/24/person_add.svg +1 -0
- package/symbols/24/photo_camera.svg +1 -0
- package/symbols/24/power_settings_new.svg +1 -0
- package/symbols/24/redo.svg +1 -0
- package/symbols/24/refresh.svg +1 -0
- package/symbols/24/remove.svg +1 -0
- package/symbols/24/save.svg +1 -0
- package/symbols/24/schedule.svg +1 -0
- package/symbols/24/search.svg +1 -0
- package/symbols/24/settings.svg +1 -0
- package/symbols/24/settings_backup_restore.svg +1 -0
- package/symbols/24/shield.svg +1 -0
- package/symbols/24/shield_person.svg +1 -0
- package/symbols/24/smart_toy.svg +1 -0
- package/symbols/24/star-fill.svg +1 -0
- package/symbols/24/star.svg +1 -0
- package/symbols/24/swap_vert.svg +1 -0
- package/symbols/24/tag.svg +1 -0
- package/symbols/24/target.svg +1 -0
- package/symbols/24/terminal.svg +1 -0
- package/symbols/24/timeline.svg +1 -0
- package/symbols/24/token.svg +1 -0
- package/symbols/24/tune.svg +1 -0
- package/symbols/24/undo.svg +1 -0
- package/symbols/24/unfold_more.svg +1 -0
- package/symbols/24/view_column.svg +1 -0
- package/symbols/24/visibility.svg +1 -0
- package/symbols/24/visibility_off.svg +1 -0
- package/symbols/24/warning-fill.svg +1 -0
- package/symbols/24/warning.svg +1 -0
- package/symbols/24/zoom_in.svg +1 -0
- package/symbols/24/zoom_out.svg +1 -0
- package/symbols/LICENSE +202 -0
- package/symbols/NOTICE +15 -0
- package/symbols/README.md +44 -0
- package/symbols/SHA256SUMS +210 -0
package/README.md
CHANGED
|
@@ -1,8 +1,91 @@
|
|
|
1
1
|
# @crewlethq/icons
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Everything a Crewlet surface draws that is not text: the Material Symbols glyph set, the marks of the applications the platform integrates with, and the Crewlet signature illustrations.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
All three are React components compiled from SVG files that ship in the package. Nothing here fetches a font, a sprite or an image from another host, so a closed network draws exactly what an open one does.
|
|
6
|
+
|
|
7
|
+
| What | Import from | Looks like |
|
|
8
|
+
| ---- | ----------- | ---------- |
|
|
9
|
+
| Glyphs (105 Material Symbols) | `@crewlethq/icons/glyphs` | `<CloseGlyph size="md" />` |
|
|
10
|
+
| A glyph chosen from a value | `@crewlethq/icons/glyphs/registry` | `glyphByName(name)` |
|
|
11
|
+
| Vendor marks | `@crewlethq/icons` | `<VendorMark vendor="slack" />` |
|
|
12
|
+
| Signature illustrations | `@crewlethq/icons` | `<AgentReading />`, `<Icon name="AgentReading" />` |
|
|
13
|
+
| The Crewlet character | `@crewlethq/icons` | `<CrewletFigure motion="wave" />` |
|
|
14
|
+
|
|
15
|
+
## Glyphs
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { CloseGlyph, KeyboardArrowDownGlyph } from '@crewlethq/icons/glyphs';
|
|
19
|
+
|
|
20
|
+
<CloseGlyph size="md" />
|
|
21
|
+
<CloseGlyph size="lg" title="Close" />
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Material Symbols Outlined, weight 400, grade 0, vendored at the 20 px and 24 px optical sizes from `google/material-design-icons`. [`symbols/README.md`](./symbols/README.md) records the provenance, the checksums and how to add or replace one.
|
|
25
|
+
|
|
26
|
+
| Prop | Type | Default | Purpose |
|
|
27
|
+
| ---- | ---- | ------- | ------- |
|
|
28
|
+
| `size` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` (12, 14, 16, 20, 24), a number of px, or any CSS length | `'1em'` | How big it draws. |
|
|
29
|
+
| `title` | string | none | Names the glyph for assistive technology. Without one the glyph is `aria-hidden` and unfocusable, which is right wherever a label sits beside it. |
|
|
30
|
+
| `opsz` | `20 \| 24` | follows `size` | Which vendored drawing to use. |
|
|
31
|
+
|
|
32
|
+
Every SVG attribute passes through, and `className` is added to the component's own `crewlet-glyph`.
|
|
33
|
+
|
|
34
|
+
**Names carry a `Glyph` suffix** because the bare words collide: `Timeline`, `List`, `Menu`, `Tag`, `Link` and `Code` are all `@crewlethq/ui` components, and a glyph taking one of those names would shadow the component at every import site that wanted both. `close` is `CloseGlyph`, `keyboard_arrow_down` is `KeyboardArrowDownGlyph`, `check_circle-fill` is `CheckCircleFillGlyph`.
|
|
35
|
+
|
|
36
|
+
**A filled state is its own name**, not a `fill` attribute: a Material Symbol is a filled path already, so `fill` paints the outline's own shape rather than solidifying it. Five glyphs ship both drawings — `check_circle` / `check_circle-fill`, `error` / `error-fill`, `info` / `info-fill`, `star` / `star-fill` and `warning` / `warning-fill` — so a surface that toggles a state swaps the component rather than a prop.
|
|
37
|
+
|
|
38
|
+
`GLYPH_NAMES` lists every vendored name and `GlyphName` is the union of them, so a name the package does not ship is a compile error.
|
|
39
|
+
|
|
40
|
+
### The optical size
|
|
41
|
+
|
|
42
|
+
The optical size axis is a different drawing, not a scaled one: the 20 px `close` is a 51-unit stroke on the 960 unit grid and the 24 px one is 56. `Glyph` takes the 20 px drawing at and below 20 px and the 24 px drawing above it, which it can work out whenever `size` says how many px it is.
|
|
43
|
+
|
|
44
|
+
It cannot when `size` is left at `1em` and an ancestor's `font-size` decides, because CSS has no way to hand a computed font size back to the component that would have to choose. The default there is the 20 px drawing, on the grounds that a glyph inheriting a font size sits in body text or a control label, every step of which is at or below 20 px. A surface that sets a larger font size passes `opsz={24}`.
|
|
45
|
+
|
|
46
|
+
### Choosing a glyph from a value
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { glyphByName } from '@crewlethq/icons/glyphs/registry';
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**This entry pulls in every glyph**, because a lookup by name is a lookup over all of them. That is the right trade where the name really is data (a navigation table, an event category, a configured integration kind) and the wrong one everywhere else, which is why it is a separate entry: code that imports one glyph never reaches it.
|
|
53
|
+
|
|
54
|
+
## Vendor marks
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import { VendorMark } from '@crewlethq/icons';
|
|
58
|
+
|
|
59
|
+
<VendorMark vendor="slack" size={24} />
|
|
60
|
+
<VendorMark vendor="datadog" size={24} muted />
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
| Prop | Type | Default | Purpose |
|
|
64
|
+
| ---- | ---- | ------- | ------- |
|
|
65
|
+
| `vendor` | `Vendor`: `'atlassian' \| 'clickup' \| 'datadog' \| 'discord' \| 'figma' \| 'github' \| 'gitlab' \| 'mattermost' \| 'microsoft-teams' \| 'notion' \| 'slack' \| 'telegram'` | required | Which mark. |
|
|
66
|
+
| `size` | as `Glyph` | `'1em'` | How big it draws. |
|
|
67
|
+
| `title` | string | none | Names the mark. Without one it is `aria-hidden`. |
|
|
68
|
+
| `muted` | boolean | `false` | Drains the colour, for an application that is available but not connected. |
|
|
69
|
+
|
|
70
|
+
**This is the one place the design system draws somebody else's colours.** Colour here carries state and never identity, and a third-party application's mark is the deliberate exception: identity is exactly what a mark is for, and a recoloured Slack mark is not Slack's. No hue in `svg/vendor/` is a token, nothing reads state from a mark, and a mark appears only beside the name of the application it belongs to.
|
|
71
|
+
|
|
72
|
+
GitHub's and Notion's marks are monochrome by design and take the current text colour, so they read on either theme. That is each vendor's own instruction rather than an exemption: both publish one mark, black on a light ground and white on a dark one, so a fixed hex would be wrong in one theme whichever was picked.
|
|
73
|
+
|
|
74
|
+
A mark keeps its own viewBox, so one that is not square (Figma's is 2:3) is drawn to fit the square `size` and centred in it, at the aspect ratio the vendor draws.
|
|
75
|
+
|
|
76
|
+
The marks and the terms on which they may be used are in [`TRADEMARKS.md`](./TRADEMARKS.md), which ships in the package. The Mattermost, Datadog, Notion, Discord, ClickUp, Telegram and Microsoft Teams drawings are Simple Icons' renderings, whose path data is CC0 1.0.
|
|
77
|
+
|
|
78
|
+
`VENDORS` lists every mark the package carries.
|
|
79
|
+
|
|
80
|
+
## Signature illustrations
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
import { AgentReading, CrewletIcon } from '@crewlethq/icons';
|
|
84
|
+
|
|
85
|
+
<AgentReading style={{ fontSize: 32 }} />
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Each illustration is a named export, and a bundler keeps only the one that is imported. There is also a by-name component:
|
|
6
89
|
|
|
7
90
|
```tsx
|
|
8
91
|
import { Icon } from '@crewlethq/icons';
|
|
@@ -10,11 +93,9 @@ import { Icon } from '@crewlethq/icons';
|
|
|
10
93
|
<Icon name="AgentReading" size="lg" title="Documentation" />
|
|
11
94
|
```
|
|
12
95
|
|
|
13
|
-
`size` accepts `'sm' | 'md' | 'lg' | 'xl'
|
|
14
|
-
|
|
15
|
-
`name` is typed as the `IconName` union, so TypeScript rejects a name the package does not ship. Plain JavaScript gets no such check, and `<Icon>` renders nothing for an unknown name, so search for a name across the whole project when an icon is renamed. `ICON_NAMES` lists every name in file-name order; treat it as a set and never index it by position, because adding or renaming an icon shifts the order.
|
|
96
|
+
`size` accepts `'sm' | 'md' | 'lg' | 'xl'` (16, 20, 24 and 32 px), a number of px, or any CSS length. `title` exposes the icon to assistive technology with `role="img"`; without it the icon is `aria-hidden`. `name` is typed as `IconName`, so a name the package does not ship is a compile error; `ICON_NAMES` lists them in file-name order, and it is a set rather than a sequence, because adding or renaming an illustration shifts the order.
|
|
16
97
|
|
|
17
|
-
|
|
98
|
+
**`Icon` carries every illustration into the bundle**, which is most of half a megabyte, nearly all of it `crewlet-reading`. A lookup by name is a lookup over all of them, so use it where the name really is data and import the component everywhere else.
|
|
18
99
|
|
|
19
100
|
### Raw SVG files
|
|
20
101
|
|
|
@@ -24,7 +105,24 @@ The source SVGs ship in the package and are exported as `@crewlethq/icons/svg/*`
|
|
|
24
105
|
import markUrl from '@crewlethq/icons/svg/crewlet-icon.svg';
|
|
25
106
|
```
|
|
26
107
|
|
|
27
|
-
A bundler resolves the import to a URL. Outside a bundler, resolve the same specifier with `import.meta.resolve('@crewlethq/icons/svg/crewlet-icon.svg')` to get the file on disk.
|
|
108
|
+
A bundler resolves the import to a URL. Outside a bundler, resolve the same specifier with `import.meta.resolve('@crewlethq/icons/svg/crewlet-icon.svg')` to get the file on disk. The vendored glyph drawings are exported the same way, as `@crewlethq/icons/symbols/20/close.svg`.
|
|
109
|
+
|
|
110
|
+
### Favicons
|
|
111
|
+
|
|
112
|
+
An icon slot is square and the mark is 3:2, so the mark sits in a band across the middle of a tab whichever file is linked: framing cannot make a wide drawing fill a square. What `favicon/` carries instead is a square SOURCE, which is what a raster pipeline and a search engine both ask for. Google considers a favicon candidate only if it is square and larger than 48px, and every generator that renders PNGs from the mark takes the padding from its viewBox rather than inventing it. The square frame is also the wide file's own side margins trimmed off, so the same paths draw about 8 percent larger in the same slot.
|
|
113
|
+
|
|
114
|
+
| File | For |
|
|
115
|
+
| ---- | --- |
|
|
116
|
+
| `favicon/crewlet.svg` | Any product's `<link rel="icon">`. The mark, framed square. |
|
|
117
|
+
| `favicon/crewlet-admin.svg` | An operator console, in a red that tells a superadmin tab from a tenant tab at a glance. |
|
|
118
|
+
| `favicon/crewlet.ico` | The raster fallback, at 16, 32 and 48 px, for a browser or a bookmark that asks for one. |
|
|
119
|
+
|
|
120
|
+
```html
|
|
121
|
+
<link rel="icon" type="image/svg+xml" href="/crewlet.svg" />
|
|
122
|
+
<link rel="icon" sizes="any" href="/favicon.ico" />
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
All three are one drawing: the square files are `svg/crewlet-icon.svg` path for path, and differ only in the viewBox that frames them and the fill. `test/brand.test.mjs` holds that, because four products each kept their own copy of the mark and copies of one drawing drift silently. It also measures the frame against the drawing inside it, so a square viewBox that crops the mark, parks it off centre or leaves it swimming in air fails rather than shipping: a favicon is the one asset nobody looks at closely enough to catch that by eye.
|
|
28
126
|
|
|
29
127
|
### CrewletFigure
|
|
30
128
|
|
|
@@ -40,24 +138,36 @@ import { CrewletFigure } from '@crewlethq/icons';
|
|
|
40
138
|
| ---- | ---- | ------- | ------- |
|
|
41
139
|
| `motion` | `CrewletMotion`: `'idle' \| 'dance' \| 'wave' \| 'jump' \| 'walk' \| 'spin'` | `'idle'` | The looping motion. |
|
|
42
140
|
| `delay` | number (seconds) | `0` | Phase offset, so several figures do not move in lockstep. |
|
|
43
|
-
| `color` | CSS colour |
|
|
141
|
+
| `color` | CSS colour | `--color-brand-mark`, falling back to the crewlet purple | Body fill. |
|
|
44
142
|
| `fillGaps` | boolean | `true` | Backs each joint with body, so a moving limb never uncovers a gap. `false` renders the plain trim, where the pieces meet with visible seams. |
|
|
45
143
|
|
|
46
|
-
The figure is sized in `em` like the icons (set `font-size` or pass `width` and `height`) and is `aria-hidden`; give the surrounding element an accessible name when the figure carries meaning. Every motion stops when the user prefers reduced motion. The geometry lives in `src/crewletParts.ts
|
|
144
|
+
The figure is sized in `em` like the icons (set `font-size` or pass `width` and `height`) and is `aria-hidden`; give the surrounding element an accessible name when the figure carries meaning. Every motion stops when the user prefers reduced motion. The geometry lives in `src/crewletParts.ts` and the motions in `src/CrewletFigure.css`, which the component imports.
|
|
145
|
+
|
|
146
|
+
## Stylesheets
|
|
147
|
+
|
|
148
|
+
Two components carry rules, and each imports its own stylesheet as a side effect, so a bundler emits the CSS beside the JavaScript and a consumer gets it without being told to. Nothing here depends on `@crewlethq/tokens`: a rule that wants a token reads it with the literal value as a fallback.
|
|
47
149
|
|
|
48
|
-
|
|
150
|
+
Loading the built entry in plain Node, with no bundler, therefore fails on the stylesheet import. That is the same contract `@crewlethq/ui` has.
|
|
49
151
|
|
|
50
|
-
|
|
152
|
+
## Add an illustration
|
|
153
|
+
|
|
154
|
+
1. Drop `<your-icon>.svg` into `svg/`. File names become PascalCase component names (for example, `agent-celebrating.svg` becomes `AgentCelebrating`).
|
|
51
155
|
2. Run `npm run build`.
|
|
52
|
-
3. The new
|
|
156
|
+
3. The new illustration is exported from `@crewlethq/icons` and appears in `ICON_NAMES`.
|
|
157
|
+
|
|
158
|
+
A vendor mark goes into `svg/vendor/` instead, and is added to the `VENDORS` list and the `Vendor` union in `src/VendorMark.tsx`. A glyph is vendored rather than drawn; see [`symbols/README.md`](./symbols/README.md).
|
|
53
159
|
|
|
54
160
|
## Conventions for source SVGs
|
|
55
161
|
|
|
56
|
-
|
|
162
|
+
Four families coexist in this package:
|
|
57
163
|
|
|
58
164
|
**Brand artwork**: `crewlet-icon` (the Crewlet mark) and `crewlet-reading` (a full-colour illustration of the Crewlet character reading a newspaper), together with `CrewletFigure`. Filled artwork on its own viewBox; render it as supplied. `crewlet-reading` is a large, detailed illustration (about 670 KB of SVG), so reserve it for hero and empty-state surfaces rather than inline icon slots. The Crewlet name, mark and character are trademarks that the MIT License does not cover; see [TRADEMARKS.md](./TRADEMARKS.md) before using them outside an official Crewlet product.
|
|
59
165
|
|
|
60
|
-
**Feature illustrations** (80x80): `hierarchy`, `
|
|
166
|
+
**Feature illustrations** (80x80): `hierarchy`, `company-as-code`, `turn-engine`, `code-sandbox`, `knowledge` and `human-in-loop`. One per capability the platform leads with, so a reader arriving from the README, the marketing site or the docs meets the same six marks. Self-contained artwork with its own gradients and background disc, so it does not inherit `currentColor`. The build prefixes every `id` in a file with that file's name, because inline SVGs share the page's id space; keep gradient and other `id` references inside the file that defines them.
|
|
167
|
+
|
|
168
|
+
A gradient in one of these **must** declare `gradientUnits="userSpaceOnUse"` and span the 80x80 canvas. Left to the SVG default, `objectBoundingBox`, a gradient is measured from the box of each shape that paints with it, and a horizontal or vertical stroke has a box with no area: the gradient degenerates and the shape does not paint at all. Several strokes in this set are exactly that, and the failure is silent, so `test/gradients.test.mjs` holds the rule over every file in `svg/`. It stops at `svg/vendor/`, where a gradient is the vendor's own and respanning it would repaint somebody else's logo; what the marks are held to instead is the failure, that no axis-aligned shape there paints from a box-measured gradient.
|
|
169
|
+
|
|
170
|
+
The gradient stops in these files carry their colour in a `style` attribute, as the drawings were authored. Rendered through the React component it is a `style` prop, which React writes through the CSSOM and `style-src` does not govern, and an SVG referenced as an image is its own document outside the page's policy either way. Inlining one of the raw files into a page served with a strict `style-src` is the case that loses the colours, so inline the component rather than the file.
|
|
61
171
|
|
|
62
172
|
**Signature illustrations** (32x32): the crewlet "agent" character in different contexts (`agent-idle`, `agent-working`, `agent-thinking`, `agent-reading`, `agent-greeting`, `agent-success`, `agent-error` and `agent-loading`). The set reads as one character through a shared line style and scale rather than an identical outline: the head and shoulders move, shrink or give way to a torso when the pose needs room for a prop (a book, a desk, a thought bubble). Name new variants `agent-<pose>.svg`.
|
|
63
173
|
|
|
@@ -68,6 +178,12 @@ Signature illustrations follow these rules so they recolor from CSS and sit on o
|
|
|
68
178
|
- `stroke-linecap="round"` and `stroke-linejoin="round"`.
|
|
69
179
|
- No hard-coded width or height attributes (the build strips them).
|
|
70
180
|
|
|
181
|
+
**Vendor marks** (`svg/vendor/`): each application's own drawing in its own colours, on its own viewBox. The root element declares the fill it wants, because the build only supplies `currentColor` where a source declares none.
|
|
182
|
+
|
|
71
183
|
## License
|
|
72
184
|
|
|
73
|
-
MIT
|
|
185
|
+
MIT AND Apache-2.0.
|
|
186
|
+
|
|
187
|
+
The package's own code and artwork are MIT; the license text is in [`LICENSE`](./LICENSE), which ships in the package. The vendored Material Symbols drawings in `symbols/` are Google's, under the Apache License 2.0, whose text and attribution are in [`symbols/LICENSE`](./symbols/LICENSE) and [`symbols/NOTICE`](./symbols/NOTICE) and ship beside them.
|
|
188
|
+
|
|
189
|
+
Both licenses cover copyright only. The Crewlet name, logo and character are trademarks, and so are the third-party marks in `svg/vendor/`. [`TRADEMARKS.md`](./TRADEMARKS.md), which also ships in the package, says how each may be used.
|
package/TRADEMARKS.md
CHANGED
|
@@ -20,7 +20,20 @@ These files in the repository carry the Crewlet logo or character:
|
|
|
20
20
|
- `packages/icons/svg/crewlet-reading.svg`
|
|
21
21
|
- `packages/icons/src/crewletParts.ts`
|
|
22
22
|
- `packages/icons/src/CrewletFigure.tsx`
|
|
23
|
+
- `packages/icons/src/CrewletFigure.css`
|
|
23
24
|
|
|
24
25
|
The `CrewletIcon`, `CrewletReading` and `CrewletFigure` components that the `@crewlethq/icons` package builds from them are covered in the same way.
|
|
25
26
|
|
|
27
|
+
## Third-party marks
|
|
28
|
+
|
|
29
|
+
`@crewlethq/icons` also ships the marks of the applications a Crewlet product integrates with, or offers as an integration, in `packages/icons/svg/vendor/`, drawn by the `VendorMark` component. Atlassian, ClickUp, Datadog, Discord, Figma, GitHub, GitLab, Mattermost, Microsoft Teams, Notion, Slack and Telegram are trademarks of their respective owners, none of whom are affiliated with Crewlet and none of whom have endorsed it.
|
|
30
|
+
|
|
31
|
+
They are here for nominative use: naming the application a feature connects to, so that a reader recognises it. That means a mark appears beside that application's own name, identifies only that application, and is never used to suggest a partnership, a sponsorship or an endorsement. Each owner's own brand guidelines apply on top of this page, and where the two differ the owner's win.
|
|
32
|
+
|
|
33
|
+
Crewlet claims no rights in these marks. They are excluded from the MIT License's grant for the same reason the Crewlet marks are: that license covers copyright, and a trademark is not copyright.
|
|
34
|
+
|
|
35
|
+
## The glyph set
|
|
36
|
+
|
|
37
|
+
The Material Symbols drawings in `packages/icons/symbols/` are Google's, under the Apache License 2.0. That license grants a copyright license and explicitly grants no trademark rights, so the drawings may be used and redistributed while "Material Symbols" and "Google" remain Google's marks. `packages/icons/symbols/LICENSE` and `NOTICE` carry the terms and ship in the package.
|
|
38
|
+
|
|
26
39
|
For any use this page does not allow, ask the maintainers first by opening an issue in this repository.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The crewlet figure's motions.
|
|
3
|
+
*
|
|
4
|
+
* A stylesheet rather than a <style> element inside the SVG, which is what
|
|
5
|
+
* this was: a style element inside an inline SVG applies to the WHOLE
|
|
6
|
+
* document, and a strict Content-Security-Policy refuses it outright, so on a
|
|
7
|
+
* page with one the figure lost every motion and its default colour with them.
|
|
8
|
+
*
|
|
9
|
+
* Every class, keyframe and custom property is namespaced under
|
|
10
|
+
* crewlet-figure, because these rules now really are the page's. To add a
|
|
11
|
+
* motion: extend CrewletMotion, add its .crewlet-figure--<name> rules and
|
|
12
|
+
* @keyframes here, and list it in the Storybook catalog.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
.crewlet-figure {
|
|
16
|
+
/* The brand mark colour where a consumer ships the tokens, and the brand's
|
|
17
|
+
own value where it does not: this package depends on no other. */
|
|
18
|
+
color: var(--color-brand-mark, #7c56ff);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.crewlet-figure__all, .crewlet-figure__arm-l, .crewlet-figure__arm-r, .crewlet-figure__leg-l, .crewlet-figure__leg-r { transform-box: view-box; animation-delay: var(--crewlet-figure-delay, 0s); }
|
|
22
|
+
.crewlet-figure__all { transform-origin: 761px 869px; }
|
|
23
|
+
.crewlet-figure__arm-l { transform-origin: 384px 473px; }
|
|
24
|
+
.crewlet-figure__arm-r { transform-origin: 1128px 475px; }
|
|
25
|
+
.crewlet-figure__leg-l { transform-origin: 588px 730px; }
|
|
26
|
+
.crewlet-figure__leg-r { transform-origin: 939px 733px; }
|
|
27
|
+
|
|
28
|
+
.crewlet-figure--idle .crewlet-figure__arm-l { animation: crewlet-figure-idle-arm-l 3.4s ease-in-out infinite; }
|
|
29
|
+
.crewlet-figure--idle .crewlet-figure__arm-r { animation: crewlet-figure-idle-arm-r 3.4s ease-in-out infinite; }
|
|
30
|
+
|
|
31
|
+
.crewlet-figure--dance .crewlet-figure__all { animation: crewlet-figure-bob 0.8s ease-in-out infinite; }
|
|
32
|
+
.crewlet-figure--dance .crewlet-figure__arm-l { animation: crewlet-figure-arm-l-dance 0.8s ease-in-out infinite; }
|
|
33
|
+
.crewlet-figure--dance .crewlet-figure__arm-r { animation: crewlet-figure-arm-r-dance 0.8s ease-in-out infinite; }
|
|
34
|
+
.crewlet-figure--dance .crewlet-figure__leg-l { animation: crewlet-figure-leg-l-dance 0.8s ease-in-out infinite; }
|
|
35
|
+
.crewlet-figure--dance .crewlet-figure__leg-r { animation: crewlet-figure-leg-r-dance 0.8s ease-in-out infinite; }
|
|
36
|
+
|
|
37
|
+
.crewlet-figure--wave .crewlet-figure__arm-r { animation: crewlet-figure-wave-arm 0.8s ease-in-out infinite; }
|
|
38
|
+
.crewlet-figure--wave .crewlet-figure__arm-l { animation: crewlet-figure-idle-arm-l 3.4s ease-in-out infinite; }
|
|
39
|
+
|
|
40
|
+
.crewlet-figure--jump .crewlet-figure__all { animation: crewlet-figure-jump 1.15s ease-in-out infinite; }
|
|
41
|
+
.crewlet-figure--jump .crewlet-figure__arm-l { animation: crewlet-figure-jump-arm-l 1.15s ease-in-out infinite; }
|
|
42
|
+
.crewlet-figure--jump .crewlet-figure__arm-r { animation: crewlet-figure-jump-arm-r 1.15s ease-in-out infinite; }
|
|
43
|
+
|
|
44
|
+
.crewlet-figure--walk .crewlet-figure__all { animation: crewlet-figure-walk-body 0.5s ease-in-out infinite; }
|
|
45
|
+
.crewlet-figure--walk .crewlet-figure__arm-l { animation: crewlet-figure-walk-arm-l 0.5s ease-in-out infinite; }
|
|
46
|
+
.crewlet-figure--walk .crewlet-figure__arm-r { animation: crewlet-figure-walk-arm-r 0.5s ease-in-out infinite; }
|
|
47
|
+
.crewlet-figure--walk .crewlet-figure__leg-l { animation: crewlet-figure-walk-leg-l 0.5s ease-in-out infinite; }
|
|
48
|
+
.crewlet-figure--walk .crewlet-figure__leg-r { animation: crewlet-figure-walk-leg-r 0.5s ease-in-out infinite; }
|
|
49
|
+
|
|
50
|
+
.crewlet-figure--spin .crewlet-figure__all { animation: crewlet-figure-spin 1.1s cubic-bezier(0.5, 0, 0.5, 1) infinite; }
|
|
51
|
+
.crewlet-figure--spin .crewlet-figure__arm-l { animation: crewlet-figure-idle-arm-l 1.1s ease-in-out infinite; }
|
|
52
|
+
.crewlet-figure--spin .crewlet-figure__arm-r { animation: crewlet-figure-idle-arm-r 1.1s ease-in-out infinite; }
|
|
53
|
+
|
|
54
|
+
@keyframes crewlet-figure-idle-arm-l { 0%, 100% { transform: rotate(2deg); } 50% { transform: rotate(-3deg); } }
|
|
55
|
+
@keyframes crewlet-figure-idle-arm-r { 0%, 100% { transform: rotate(-2deg); } 50% { transform: rotate(3deg); } }
|
|
56
|
+
@keyframes crewlet-figure-arm-l-dance { 0%, 100% { transform: rotate(6deg); } 50% { transform: rotate(-12deg); } }
|
|
57
|
+
@keyframes crewlet-figure-arm-r-dance { 0%, 100% { transform: rotate(-6deg); } 50% { transform: rotate(12deg); } }
|
|
58
|
+
@keyframes crewlet-figure-leg-l-dance { 0%, 100% { transform: rotate(-8deg); } 50% { transform: rotate(8deg); } }
|
|
59
|
+
@keyframes crewlet-figure-leg-r-dance { 0%, 100% { transform: rotate(8deg); } 50% { transform: rotate(-8deg); } }
|
|
60
|
+
@keyframes crewlet-figure-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-26px); } }
|
|
61
|
+
@keyframes crewlet-figure-wave-arm { 0%, 100% { transform: rotate(-9deg); } 50% { transform: rotate(-20deg); } }
|
|
62
|
+
@keyframes crewlet-figure-jump {
|
|
63
|
+
0%, 100% { transform: translateY(0) scale(1, 1); }
|
|
64
|
+
12% { transform: translateY(22px) scale(1.05, 0.9); }
|
|
65
|
+
45% { transform: translateY(-124px) scale(0.95, 1.06); }
|
|
66
|
+
72% { transform: translateY(0) scale(1.03, 0.96); }
|
|
67
|
+
}
|
|
68
|
+
@keyframes crewlet-figure-jump-arm-l { 0%, 100% { transform: rotate(3deg); } 45% { transform: rotate(-16deg); } }
|
|
69
|
+
@keyframes crewlet-figure-jump-arm-r { 0%, 100% { transform: rotate(-3deg); } 45% { transform: rotate(16deg); } }
|
|
70
|
+
@keyframes crewlet-figure-walk-body { 0%, 100% { transform: translateY(0) rotate(-2deg); } 50% { transform: translateY(-10px) rotate(2deg); } }
|
|
71
|
+
@keyframes crewlet-figure-walk-leg-l { 0%, 100% { transform: rotate(16deg); } 50% { transform: rotate(-16deg); } }
|
|
72
|
+
@keyframes crewlet-figure-walk-leg-r { 0%, 100% { transform: rotate(-16deg); } 50% { transform: rotate(16deg); } }
|
|
73
|
+
@keyframes crewlet-figure-walk-arm-l { 0%, 100% { transform: rotate(-12deg); } 50% { transform: rotate(12deg); } }
|
|
74
|
+
@keyframes crewlet-figure-walk-arm-r { 0%, 100% { transform: rotate(12deg); } 50% { transform: rotate(-12deg); } }
|
|
75
|
+
@keyframes crewlet-figure-spin {
|
|
76
|
+
0% { transform: rotate(0) translateY(0); }
|
|
77
|
+
25% { transform: rotate(90deg) translateY(-40px); }
|
|
78
|
+
50% { transform: rotate(180deg) translateY(0); }
|
|
79
|
+
75% { transform: rotate(270deg) translateY(-40px); }
|
|
80
|
+
100% { transform: rotate(360deg) translateY(0); }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@media (prefers-reduced-motion: reduce) {
|
|
84
|
+
.crewlet-figure__all, .crewlet-figure__arm-l, .crewlet-figure__arm-r, .crewlet-figure__leg-l, .crewlet-figure__leg-r { animation: none !important; }
|
|
85
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { SVGProps } from 'react';
|
|
2
|
+
|
|
3
|
+
/** The size steps, in px. The type scale's own steps, not a new scale. */
|
|
4
|
+
declare const GLYPH_SIZES: {
|
|
5
|
+
readonly xs: 12;
|
|
6
|
+
readonly sm: 14;
|
|
7
|
+
readonly md: 16;
|
|
8
|
+
readonly lg: 20;
|
|
9
|
+
readonly xl: 24;
|
|
10
|
+
};
|
|
11
|
+
type GlyphSizeStep = keyof typeof GLYPH_SIZES;
|
|
12
|
+
type GlyphSize = GlyphSizeStep | number | (string & {});
|
|
13
|
+
/**
|
|
14
|
+
* Which drawing to use. The optical size axis is a different drawing rather
|
|
15
|
+
* than a scaled one: the 20 px `close` is a 51-unit stroke on the 960 grid and
|
|
16
|
+
* the 24 px one is 56, so the wrong drawing reads as the wrong weight beside
|
|
17
|
+
* anything drawing the right one.
|
|
18
|
+
*/
|
|
19
|
+
type GlyphOpticalSize = 20 | 24;
|
|
20
|
+
interface GlyphProps extends Omit<SVGProps<SVGSVGElement>, 'title'> {
|
|
21
|
+
/** A step, a number of px, or any CSS length. Defaults to `1em`. */
|
|
22
|
+
size?: GlyphSize;
|
|
23
|
+
/**
|
|
24
|
+
* Names the glyph for assistive technology. Without one the glyph is
|
|
25
|
+
* decoration, which is what it almost always is: the meaning is carried by
|
|
26
|
+
* the label beside it.
|
|
27
|
+
*/
|
|
28
|
+
title?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Which vendored drawing to render. It follows `size` whenever `size` says
|
|
31
|
+
* how many px that is, so pass this only when it cannot: a glyph left at the
|
|
32
|
+
* default `1em` inside a surface whose own stylesheet sets a font size above
|
|
33
|
+
* 20 px wants `opsz={24}`. CSS cannot hand a computed font size back to the
|
|
34
|
+
* component that would have to choose, so the choice is a prop.
|
|
35
|
+
*/
|
|
36
|
+
opsz?: GlyphOpticalSize;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The px a size resolves to, or null when it does not resolve to one. `1em`,
|
|
40
|
+
* `2rem` and `100%` each depend on something only the document knows, so they
|
|
41
|
+
* answer null and leave the optical size to its default.
|
|
42
|
+
*/
|
|
43
|
+
declare function glyphPixels(size: GlyphSize | undefined): number | null;
|
|
44
|
+
/** The drawing a rendered size asks for. */
|
|
45
|
+
declare function glyphOpticalSize(size: GlyphSize | undefined): GlyphOpticalSize;
|
|
46
|
+
/**
|
|
47
|
+
* The CSS length a size prop means, `1em` when it says nothing.
|
|
48
|
+
*
|
|
49
|
+
* Shared with VendorMark, which sits on the same steps because it sits beside
|
|
50
|
+
* glyphs, and PUBLISHED for the same reason: a drawing that is not a Glyph at
|
|
51
|
+
* all (the Crewlet figure, a vendor's own SVG) is sized by a consumer who is
|
|
52
|
+
* choosing between it and a glyph, and a second reading of what `sm` is worth
|
|
53
|
+
* is how one of them stops matching the other.
|
|
54
|
+
*/
|
|
55
|
+
declare function cssLength(size: GlyphSize | undefined): string;
|
|
56
|
+
|
|
57
|
+
export { type GlyphProps as G, GLYPH_SIZES as a, type GlyphOpticalSize as b, type GlyphSize as c, type GlyphSizeStep as d, cssLength as e, glyphPixels as f, glyphOpticalSize as g };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* A vendor mark draws the app's own colours, so the muted state cannot be a
|
|
3
|
+
* colour: it drains the one the mark already has. That keeps "available but
|
|
4
|
+
* not connected" readable without inventing a second palette for somebody
|
|
5
|
+
* else's brand, and it leaves the drawing recognisable, which a flat tint
|
|
6
|
+
* would not.
|
|
7
|
+
*
|
|
8
|
+
* A rule rather than a style attribute, because a strict Content-Security-
|
|
9
|
+
* Policy that allows a stylesheet still refuses an inline style.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
.crewlet-vendor-mark {
|
|
13
|
+
display: inline-block;
|
|
14
|
+
flex: none;
|
|
15
|
+
vertical-align: middle;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.crewlet-vendor-mark--muted {
|
|
19
|
+
filter: grayscale(1);
|
|
20
|
+
opacity: 0.55;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
* Forced colours drop filters and repaint everything from the user's own
|
|
25
|
+
* palette, so the muted mark would come back at full strength and read as
|
|
26
|
+
* connected. Opacity survives, and carries the distinction on its own.
|
|
27
|
+
*/
|
|
28
|
+
@media (forced-colors: active) {
|
|
29
|
+
.crewlet-vendor-mark--muted {
|
|
30
|
+
opacity: 0.4;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
// src/Glyph.tsx
|
|
4
|
+
var GLYPH_SIZES = { xs: 12, sm: 14, md: 16, lg: 20, xl: 24 };
|
|
5
|
+
var THRESHOLD = 20;
|
|
6
|
+
function glyphPixels(size) {
|
|
7
|
+
if (size === void 0) return null;
|
|
8
|
+
if (typeof size === "number") return Number.isFinite(size) ? size : null;
|
|
9
|
+
if (size in GLYPH_SIZES) return GLYPH_SIZES[size];
|
|
10
|
+
const px = /^(\d+(?:\.\d+)?)px$/.exec(size.trim());
|
|
11
|
+
return px === null ? null : Number(px[1]);
|
|
12
|
+
}
|
|
13
|
+
function glyphOpticalSize(size) {
|
|
14
|
+
const pixels = glyphPixels(size);
|
|
15
|
+
return pixels !== null && pixels > THRESHOLD ? 24 : 20;
|
|
16
|
+
}
|
|
17
|
+
function cssLength(size) {
|
|
18
|
+
if (size === void 0) return "1em";
|
|
19
|
+
if (typeof size === "number") return `${size}px`;
|
|
20
|
+
if (size in GLYPH_SIZES) return `${GLYPH_SIZES[size]}px`;
|
|
21
|
+
return size;
|
|
22
|
+
}
|
|
23
|
+
function Glyph({
|
|
24
|
+
drawing,
|
|
25
|
+
size,
|
|
26
|
+
title,
|
|
27
|
+
opsz,
|
|
28
|
+
className,
|
|
29
|
+
...rest
|
|
30
|
+
}) {
|
|
31
|
+
const dimension = cssLength(size);
|
|
32
|
+
const semantics = title ? { role: "img", "aria-label": title } : { "aria-hidden": true, focusable: false };
|
|
33
|
+
return /* @__PURE__ */ jsx(
|
|
34
|
+
"svg",
|
|
35
|
+
{
|
|
36
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
37
|
+
viewBox: "0 -960 960 960",
|
|
38
|
+
width: dimension,
|
|
39
|
+
height: dimension,
|
|
40
|
+
fill: "currentColor",
|
|
41
|
+
...semantics,
|
|
42
|
+
className: className === void 0 ? "crewlet-glyph" : `crewlet-glyph ${className}`,
|
|
43
|
+
...rest,
|
|
44
|
+
children: /* @__PURE__ */ jsx("path", { d: drawing[opsz ?? glyphOpticalSize(size)] })
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { GLYPH_SIZES, Glyph, cssLength, glyphOpticalSize, glyphPixels };
|