@lovett/ui 0.0.9 → 0.0.11
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/dist/index.d.ts +823 -135
- package/dist/index.js +2048 -358
- package/dist/index.js.map +1 -1
- package/dist/styles.css +44 -2
- package/dist/theme-v2.css +228 -0
- package/dist/tokens.css +123 -8
- package/package.json +1 -1
- package/src/__tests__/anchor.test.tsx +422 -0
- package/src/__tests__/combobox.test.tsx +677 -0
- package/src/__tests__/dropdown-menu.test.tsx +418 -0
- package/src/__tests__/helpers/geometry.ts +58 -0
- package/src/__tests__/layer-stack.test.tsx +228 -0
- package/src/__tests__/modal.test.tsx +180 -6
- package/src/__tests__/popover.test.tsx +460 -0
- package/src/__tests__/select.test.tsx +543 -0
- package/src/__tests__/tooltip.test.tsx +355 -0
- package/src/calculator-shell-v2.tsx +19 -39
- package/src/code-block.tsx +15 -26
- package/src/combobox.tsx +796 -0
- package/src/dropdown-menu.tsx +142 -152
- package/src/icons/brand.tsx +81 -2
- package/src/index.ts +111 -0
- package/src/lib/anchor.ts +427 -0
- package/src/lib/focus.ts +32 -0
- package/src/lib/layer-stack.ts +188 -0
- package/src/lib/refs.ts +31 -0
- package/src/metric-card.tsx +57 -22
- package/src/modal.tsx +149 -9
- package/src/page-shell.tsx +91 -2
- package/src/popover.tsx +407 -0
- package/src/segmented-pill.tsx +33 -10
- package/src/select.tsx +646 -0
- package/src/stat-row.tsx +108 -70
- package/src/styles.css +44 -2
- package/src/theme-v2.css +7 -245
- package/src/tokens.css +123 -8
- package/src/tooltip.tsx +297 -0
- package/src/react-syntax-highlighter-prism.d.ts +0 -34
- package/src/v2/README.md +0 -208
- package/src/v2/__demo__/showcase.tsx +0 -1045
- package/src/v2/action.tsx +0 -91
- package/src/v2/callout.tsx +0 -76
- package/src/v2/document-section.tsx +0 -82
- package/src/v2/document-shell.tsx +0 -0
- package/src/v2/field-row.tsx +0 -113
- package/src/v2/icons.tsx +0 -165
- package/src/v2/index.ts +0 -147
- package/src/v2/layout.tsx +0 -293
- package/src/v2/progress-track.tsx +0 -89
- package/src/v2/stat-tile.tsx +0 -129
- package/src/v2/states.tsx +0 -271
- package/src/v2/status-pill.tsx +0 -74
- package/src/v2/theme.css +0 -1861
- package/src/v2/timeline.tsx +0 -81
- package/src/v2/tokens.ts +0 -228
package/src/dropdown-menu.tsx
CHANGED
|
@@ -8,45 +8,75 @@
|
|
|
8
8
|
* BUILD_FRESH per ADR-025 DEVIATION-3: source ships on
|
|
9
9
|
* `@base-ui/react/menu`, which is NOT in CLAUDE.md §1 allowed deps.
|
|
10
10
|
* The compound-component surface (Root / Trigger / Content / Item /
|
|
11
|
-
* CheckboxItem / Separator / Label / Group) carries over
|
|
12
|
-
* implementation is a lightweight React Context + Portal + outside-
|
|
13
|
-
* click + Escape handler.
|
|
11
|
+
* CheckboxItem / Separator / Label / Group) carries over.
|
|
14
12
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
13
|
+
* Rebuilt on the shared anchored-positioning kit per ADR-0030 Decision G
|
|
14
|
+
* (meta-ads-audit-dashboard task manager; the workspace app's 137 call
|
|
15
|
+
* sites are the other consumer). Same public API, same DOM roles and
|
|
16
|
+
* `data-slot`s; what changed underneath:
|
|
17
|
+
* • Positioning is `useAnchoredPosition` (lib/anchor.ts) — the menu now
|
|
18
|
+
* FLIPS to the other side when it cannot fit, instead of being clamped
|
|
19
|
+
* over its own trigger; `side` accepts `left` / `right` too.
|
|
20
|
+
* • Escape goes through the shared dismiss-layer stack (lib/layer-stack.ts)
|
|
21
|
+
* that Modal uses: a menu opened inside a Modal now closes on its own,
|
|
22
|
+
* leaving the Modal open. Before, both closed on one keypress.
|
|
23
|
+
* • Outside-click is `useOutsideClick`, layer-aware — a Select or Popover
|
|
24
|
+
* opened from a menu item is not "outside".
|
|
25
|
+
* • Trigger, Content, Item and CheckboxItem forward refs.
|
|
26
|
+
*
|
|
27
|
+
* Trigger styling per ADR-026 D4 (DQ-4 LIFT): the default trigger inherits
|
|
28
|
+
* the secondary Button visual (rounded-xl, `--bg-card` fill, 1px
|
|
29
|
+
* `--border`). Consumers can pass custom trigger content; the wrapper
|
|
30
|
+
* applies the button-style classes unless `unstyled` is set.
|
|
31
|
+
*
|
|
32
|
+
* Focus stays on the trigger when the menu opens (menus do not steal
|
|
33
|
+
* focus here; items are reached with Tab). Escape closes and refocuses the
|
|
34
|
+
* trigger. Roving arrow-key focus is a follow-up.
|
|
35
|
+
*
|
|
36
|
+
* Token discipline: surface chrome is `POPOVER_SURFACE_STYLE` (shared with
|
|
37
|
+
* Popover — internal `--popover` tokens are allowed inside packages/ui);
|
|
38
|
+
* item hover / focus use public `--surface-overlay`; motion is
|
|
39
|
+
* `.ds-enter-pop`, gated on `positioned` so it never plays from the parked
|
|
40
|
+
* off-screen frame.
|
|
20
41
|
*
|
|
21
42
|
* Workspace ADR: ADR-025 (port) + ADR-026 D4 (trigger spec)
|
|
22
|
-
* Ported 2026-05-18
|
|
43
|
+
* Ported 2026-05-18 · Rebuilt on lib/anchor 2026-09-05
|
|
23
44
|
*/
|
|
24
45
|
|
|
25
46
|
import {
|
|
26
47
|
createContext,
|
|
48
|
+
forwardRef,
|
|
27
49
|
useCallback,
|
|
28
50
|
useContext,
|
|
29
|
-
useEffect,
|
|
30
51
|
useId,
|
|
31
|
-
useLayoutEffect,
|
|
32
52
|
useMemo,
|
|
33
53
|
useRef,
|
|
34
54
|
useState,
|
|
35
55
|
type ButtonHTMLAttributes,
|
|
36
56
|
type HTMLAttributes,
|
|
37
57
|
type ReactNode,
|
|
58
|
+
type RefObject,
|
|
38
59
|
} from 'react'
|
|
39
60
|
import { createPortal } from 'react-dom'
|
|
40
61
|
import { Check } from 'lucide-react'
|
|
41
62
|
import { cn } from './lib/utils'
|
|
63
|
+
import { composeRefs } from './lib/refs'
|
|
64
|
+
import { useLayer } from './lib/layer-stack'
|
|
65
|
+
import {
|
|
66
|
+
useAnchoredPosition,
|
|
67
|
+
useOutsideClick,
|
|
68
|
+
type AnchorAlign,
|
|
69
|
+
type AnchorSide,
|
|
70
|
+
} from './lib/anchor'
|
|
71
|
+
import { POPOVER_SURFACE_STYLE } from './popover'
|
|
42
72
|
|
|
43
|
-
type Align =
|
|
44
|
-
type Side =
|
|
73
|
+
type Align = AnchorAlign
|
|
74
|
+
type Side = AnchorSide
|
|
45
75
|
|
|
46
76
|
interface DropdownMenuContextValue {
|
|
47
77
|
open: boolean
|
|
48
78
|
setOpen: (next: boolean) => void
|
|
49
|
-
triggerRef:
|
|
79
|
+
triggerRef: RefObject<HTMLButtonElement | null>
|
|
50
80
|
contentId: string
|
|
51
81
|
}
|
|
52
82
|
|
|
@@ -119,23 +149,28 @@ export interface DropdownMenuTriggerProps
|
|
|
119
149
|
* secondary Button visual (rounded-xl + `--bg-card` fill + 1px
|
|
120
150
|
* `--border`). Set `unstyled` to wrap your own button shape.
|
|
121
151
|
*/
|
|
122
|
-
export
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
children,
|
|
127
|
-
|
|
128
|
-
|
|
152
|
+
export const DropdownMenuTrigger = forwardRef<
|
|
153
|
+
HTMLButtonElement,
|
|
154
|
+
DropdownMenuTriggerProps
|
|
155
|
+
>(function DropdownMenuTrigger(
|
|
156
|
+
{ unstyled, className, onClick, children, ...rest },
|
|
157
|
+
forwardedRef,
|
|
158
|
+
) {
|
|
129
159
|
const { open, setOpen, triggerRef, contentId } =
|
|
130
160
|
useDropdownMenuCtx('DropdownMenuTrigger')
|
|
161
|
+
const ref = useMemo(
|
|
162
|
+
() => composeRefs<HTMLButtonElement>(forwardedRef, triggerRef),
|
|
163
|
+
[forwardedRef, triggerRef],
|
|
164
|
+
)
|
|
131
165
|
|
|
132
166
|
return (
|
|
133
167
|
<button
|
|
134
|
-
ref={
|
|
168
|
+
ref={ref}
|
|
135
169
|
type="button"
|
|
136
170
|
aria-haspopup="menu"
|
|
137
171
|
aria-expanded={open}
|
|
138
172
|
aria-controls={open ? contentId : undefined}
|
|
173
|
+
data-state={open ? 'open' : 'closed'}
|
|
139
174
|
className={cn(
|
|
140
175
|
!unstyled && 'btn btn-secondary',
|
|
141
176
|
className,
|
|
@@ -149,7 +184,7 @@ export function DropdownMenuTrigger({
|
|
|
149
184
|
{children}
|
|
150
185
|
</button>
|
|
151
186
|
)
|
|
152
|
-
}
|
|
187
|
+
})
|
|
153
188
|
|
|
154
189
|
export interface DropdownMenuContentProps
|
|
155
190
|
extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
@@ -158,142 +193,98 @@ export interface DropdownMenuContentProps
|
|
|
158
193
|
align?: Align
|
|
159
194
|
/** Pixel offset along the alignment axis. Default 0. */
|
|
160
195
|
alignOffset?: number
|
|
161
|
-
/**
|
|
196
|
+
/**
|
|
197
|
+
* Preferred side of the trigger. Default `bottom`. Flips to the opposite
|
|
198
|
+
* side when it cannot fit (before the 2026-09-05 rebuild it was clamped
|
|
199
|
+
* over its own trigger instead; only that no-room case differs).
|
|
200
|
+
*/
|
|
162
201
|
side?: Side
|
|
163
202
|
/** Pixel offset along the side axis. Default 6. */
|
|
164
203
|
sideOffset?: number
|
|
165
204
|
}
|
|
166
205
|
|
|
167
|
-
export
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
206
|
+
export const DropdownMenuContent = forwardRef<
|
|
207
|
+
HTMLDivElement,
|
|
208
|
+
DropdownMenuContentProps
|
|
209
|
+
>(function DropdownMenuContent(
|
|
210
|
+
{
|
|
211
|
+
children,
|
|
212
|
+
align = 'start',
|
|
213
|
+
alignOffset = 0,
|
|
214
|
+
side = 'bottom',
|
|
215
|
+
sideOffset = 6,
|
|
216
|
+
className,
|
|
217
|
+
style,
|
|
218
|
+
...rest
|
|
219
|
+
},
|
|
220
|
+
forwardedRef,
|
|
221
|
+
) {
|
|
177
222
|
const { open, setOpen, triggerRef, contentId } =
|
|
178
223
|
useDropdownMenuCtx('DropdownMenuContent')
|
|
179
224
|
const contentRef = useRef<HTMLDivElement | null>(null)
|
|
180
|
-
const [position, setPosition] = useState<{ left: number; top: number } | null>(
|
|
181
|
-
null,
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
// Position relative to the trigger viewport rect. We measure on
|
|
185
|
-
// open + on layout changes (scroll / resize) so the panel tracks.
|
|
186
|
-
useLayoutEffect(() => {
|
|
187
|
-
if (!open) {
|
|
188
|
-
setPosition(null)
|
|
189
|
-
return
|
|
190
|
-
}
|
|
191
|
-
const measure = () => {
|
|
192
|
-
const triggerEl = triggerRef.current
|
|
193
|
-
const contentEl = contentRef.current
|
|
194
|
-
if (!triggerEl || !contentEl) return
|
|
195
|
-
const triggerRect = triggerEl.getBoundingClientRect()
|
|
196
|
-
const contentRect = contentEl.getBoundingClientRect()
|
|
197
|
-
|
|
198
|
-
let left = triggerRect.left + alignOffset
|
|
199
|
-
if (align === 'end') left = triggerRect.right - contentRect.width - alignOffset
|
|
200
|
-
else if (align === 'center')
|
|
201
|
-
left =
|
|
202
|
-
triggerRect.left +
|
|
203
|
-
triggerRect.width / 2 -
|
|
204
|
-
contentRect.width / 2 +
|
|
205
|
-
alignOffset
|
|
206
|
-
|
|
207
|
-
let top =
|
|
208
|
-
side === 'bottom'
|
|
209
|
-
? triggerRect.bottom + sideOffset
|
|
210
|
-
: triggerRect.top - contentRect.height - sideOffset
|
|
211
225
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
226
|
+
const {
|
|
227
|
+
ref: positionRef,
|
|
228
|
+
style: positionStyle,
|
|
229
|
+
placement,
|
|
230
|
+
positioned,
|
|
231
|
+
} = useAnchoredPosition<HTMLDivElement>({
|
|
232
|
+
anchorRef: triggerRef,
|
|
233
|
+
side,
|
|
234
|
+
align,
|
|
235
|
+
offset: sideOffset,
|
|
236
|
+
alignOffset,
|
|
237
|
+
enabled: open,
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
// Topmost-wins Escape, shared with Modal and Popover.
|
|
241
|
+
const layer = useLayer({
|
|
242
|
+
enabled: open,
|
|
243
|
+
kind: 'popover',
|
|
244
|
+
elementRef: contentRef,
|
|
245
|
+
onEscape: () => {
|
|
246
|
+
setOpen(false)
|
|
247
|
+
triggerRef.current?.focus()
|
|
248
|
+
},
|
|
249
|
+
})
|
|
221
250
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
window.removeEventListener('resize', measure)
|
|
227
|
-
window.removeEventListener('scroll', measure, true)
|
|
228
|
-
}
|
|
229
|
-
}, [open, align, alignOffset, side, sideOffset, triggerRef])
|
|
251
|
+
useOutsideClick([triggerRef, contentRef], () => setOpen(false), {
|
|
252
|
+
enabled: open,
|
|
253
|
+
layer,
|
|
254
|
+
})
|
|
230
255
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const target = event.target as Node | null
|
|
236
|
-
if (!target) return
|
|
237
|
-
if (contentRef.current?.contains(target)) return
|
|
238
|
-
if (triggerRef.current?.contains(target)) return
|
|
239
|
-
setOpen(false)
|
|
240
|
-
}
|
|
241
|
-
const onKeyDown = (event: KeyboardEvent) => {
|
|
242
|
-
if (event.key === 'Escape') {
|
|
243
|
-
event.stopPropagation()
|
|
244
|
-
setOpen(false)
|
|
245
|
-
triggerRef.current?.focus()
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
document.addEventListener('pointerdown', onPointerDown)
|
|
249
|
-
document.addEventListener('keydown', onKeyDown)
|
|
250
|
-
return () => {
|
|
251
|
-
document.removeEventListener('pointerdown', onPointerDown)
|
|
252
|
-
document.removeEventListener('keydown', onKeyDown)
|
|
253
|
-
}
|
|
254
|
-
}, [open, setOpen, triggerRef])
|
|
256
|
+
const ref = useMemo(
|
|
257
|
+
() => composeRefs<HTMLDivElement>(forwardedRef, contentRef, positionRef),
|
|
258
|
+
[forwardedRef, positionRef],
|
|
259
|
+
)
|
|
255
260
|
|
|
256
261
|
if (!open || typeof document === 'undefined') return null
|
|
257
262
|
|
|
258
|
-
// Two-phase render:
|
|
259
|
-
//
|
|
260
|
-
//
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
// caused the "flash at top of viewport then jump under the trigger"
|
|
264
|
-
// glitch.
|
|
265
|
-
const isPositioned = position !== null
|
|
266
|
-
|
|
263
|
+
// Two-phase render (see lib/anchor.ts): the hook parks the first paint
|
|
264
|
+
// off-screen and hidden, measures, then applies real coordinates. The
|
|
265
|
+
// entrance animation is gated on `positioned` so it never fires from the
|
|
266
|
+
// parked location — that was the "flash at top of viewport then jump
|
|
267
|
+
// under the trigger" glitch.
|
|
267
268
|
return createPortal(
|
|
268
269
|
<div
|
|
269
|
-
ref={
|
|
270
|
+
ref={ref}
|
|
270
271
|
id={contentId}
|
|
271
272
|
role="menu"
|
|
272
273
|
data-slot="dropdown-menu-content"
|
|
273
274
|
data-state="open"
|
|
274
|
-
data-
|
|
275
|
+
data-side={placement?.side}
|
|
276
|
+
data-align={placement?.align}
|
|
277
|
+
data-positioned={positioned ? 'true' : 'false'}
|
|
275
278
|
className={cn(
|
|
276
279
|
'fixed z-[100] min-w-48 max-w-[min(28rem,calc(100vw-16px))]',
|
|
277
280
|
'p-1.5 overflow-y-auto',
|
|
278
281
|
'rounded-[var(--radius-lg)]',
|
|
279
|
-
|
|
282
|
+
positioned && 'ds-enter-pop',
|
|
280
283
|
className,
|
|
281
284
|
)}
|
|
282
285
|
style={{
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
// visibility:hidden during a same-frame double-render. If the
|
|
286
|
-
// first paint slips through, it's at (-99999, -99999), not at
|
|
287
|
-
// viewport (0, 0) where a left-edge flash would be visible.
|
|
288
|
-
left: position?.left ?? -99999,
|
|
289
|
-
top: position?.top ?? -99999,
|
|
290
|
-
opacity: isPositioned ? undefined : 0,
|
|
291
|
-
pointerEvents: isPositioned ? undefined : 'none',
|
|
292
|
-
visibility: isPositioned ? 'visible' : 'hidden',
|
|
293
|
-
background: 'rgb(var(--popover))',
|
|
294
|
-
color: 'rgb(var(--popover-foreground))',
|
|
295
|
-
border: '1px solid rgb(var(--border))',
|
|
296
|
-
boxShadow: 'var(--shadow-lg, 0 10px 32px rgba(0,0,0,0.18))',
|
|
286
|
+
...positionStyle,
|
|
287
|
+
...POPOVER_SURFACE_STYLE,
|
|
297
288
|
...style,
|
|
298
289
|
}}
|
|
299
290
|
{...rest}
|
|
@@ -302,7 +293,7 @@ export function DropdownMenuContent({
|
|
|
302
293
|
</div>,
|
|
303
294
|
document.body,
|
|
304
295
|
)
|
|
305
|
-
}
|
|
296
|
+
})
|
|
306
297
|
|
|
307
298
|
export interface DropdownMenuItemProps
|
|
308
299
|
extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
@@ -312,17 +303,17 @@ export interface DropdownMenuItemProps
|
|
|
312
303
|
inset?: boolean
|
|
313
304
|
}
|
|
314
305
|
|
|
315
|
-
export
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
onClick,
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
}: DropdownMenuItemProps) {
|
|
306
|
+
export const DropdownMenuItem = forwardRef<
|
|
307
|
+
HTMLButtonElement,
|
|
308
|
+
DropdownMenuItemProps
|
|
309
|
+
>(function DropdownMenuItem(
|
|
310
|
+
{ variant = 'default', inset, className, onClick, children, ...rest },
|
|
311
|
+
forwardedRef,
|
|
312
|
+
) {
|
|
323
313
|
const { setOpen } = useDropdownMenuCtx('DropdownMenuItem')
|
|
324
314
|
return (
|
|
325
315
|
<button
|
|
316
|
+
ref={forwardedRef}
|
|
326
317
|
role="menuitem"
|
|
327
318
|
type="button"
|
|
328
319
|
data-slot="dropdown-menu-item"
|
|
@@ -348,7 +339,7 @@ export function DropdownMenuItem({
|
|
|
348
339
|
{children}
|
|
349
340
|
</button>
|
|
350
341
|
)
|
|
351
|
-
}
|
|
342
|
+
})
|
|
352
343
|
|
|
353
344
|
export interface DropdownMenuCheckboxItemProps
|
|
354
345
|
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onChange'> {
|
|
@@ -356,17 +347,16 @@ export interface DropdownMenuCheckboxItemProps
|
|
|
356
347
|
onCheckedChange?: (checked: boolean) => void
|
|
357
348
|
}
|
|
358
349
|
|
|
359
|
-
export
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
children,
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
...rest
|
|
367
|
-
}: DropdownMenuCheckboxItemProps) {
|
|
350
|
+
export const DropdownMenuCheckboxItem = forwardRef<
|
|
351
|
+
HTMLButtonElement,
|
|
352
|
+
DropdownMenuCheckboxItemProps
|
|
353
|
+
>(function DropdownMenuCheckboxItem(
|
|
354
|
+
{ checked, onCheckedChange, className, children, onClick, disabled, ...rest },
|
|
355
|
+
forwardedRef,
|
|
356
|
+
) {
|
|
368
357
|
return (
|
|
369
358
|
<button
|
|
359
|
+
ref={forwardedRef}
|
|
370
360
|
role="menuitemcheckbox"
|
|
371
361
|
type="button"
|
|
372
362
|
data-slot="dropdown-menu-checkbox-item"
|
|
@@ -399,7 +389,7 @@ export function DropdownMenuCheckboxItem({
|
|
|
399
389
|
) : null}
|
|
400
390
|
</button>
|
|
401
391
|
)
|
|
402
|
-
}
|
|
392
|
+
})
|
|
403
393
|
|
|
404
394
|
export function DropdownMenuSeparator({
|
|
405
395
|
className,
|
package/src/icons/brand.tsx
CHANGED
|
@@ -18,11 +18,13 @@
|
|
|
18
18
|
* platforms themselves, exactly like the platform-chrome in the ad previews.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
+
import type { ReactElement } from 'react'
|
|
22
|
+
|
|
21
23
|
export interface BrandIconProps {
|
|
22
24
|
/** Square box size in px; the glyph letterboxes inside (non-square viewBoxes
|
|
23
25
|
* centre via preserveAspectRatio). Default 16 — a tab/affix glyph. */
|
|
24
|
-
size?: number
|
|
25
|
-
className?: string
|
|
26
|
+
size?: number | undefined
|
|
27
|
+
className?: string | undefined
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
/** Meta brand glyph (mono, currentColor). */
|
|
@@ -185,3 +187,80 @@ export const BRAND_ICONS = {
|
|
|
185
187
|
} as const
|
|
186
188
|
|
|
187
189
|
export type BrandIconKey = keyof typeof BRAND_ICONS
|
|
190
|
+
|
|
191
|
+
// ─── Full-colour marks ───────────────────────────────────────────────────
|
|
192
|
+
//
|
|
193
|
+
// The mono glyphs above are for CONTEXT — inside a red pill, on a tab, next to
|
|
194
|
+
// a label — where a full-colour mark would clash and where `currentColor` is
|
|
195
|
+
// the whole point. These are for the opposite case: a mark standing alone in a
|
|
196
|
+
// list, where colour is what makes a platform recognisable at 16px and there is
|
|
197
|
+
// no surrounding colour to fight.
|
|
198
|
+
//
|
|
199
|
+
// Same source (the owner's logos-apps fork), same nominative use. Three ship
|
|
200
|
+
// monochrome upstream — Instagram, Snapchat, X — so their fills are already
|
|
201
|
+
// `currentColor` here and they follow the theme; baked black would vanish in
|
|
202
|
+
// dark mode, and both are legitimate presentations of those marks.
|
|
203
|
+
//
|
|
204
|
+
// Added 2026-08-30. These began life as a separate `platform-logo.tsx` written
|
|
205
|
+
// without noticing ADR-135 had already solved half the problem. Folded in here
|
|
206
|
+
// rather than left as a rival module: two files of brand marks from one source
|
|
207
|
+
// is how a design system ends up with an Instagram that looks different on two
|
|
208
|
+
// screens.
|
|
209
|
+
|
|
210
|
+
interface ColorMark {
|
|
211
|
+
viewBox: string
|
|
212
|
+
path: ReactElement
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const COLOR_MARKS: Record<string, ColorMark> = {
|
|
216
|
+
instagram: { viewBox: '0 0 256 256', path: (<><path fill="currentColor" d="M128 23.064c34.177 0 38.225.13 51.722.745c12.48.57 19.258 2.655 23.769 4.408c5.974 2.322 10.238 5.096 14.717 9.575s7.253 8.743 9.575 14.717c1.753 4.511 3.838 11.289 4.408 23.768c.615 13.498.745 17.546.745 51.723s-.13 38.226-.745 51.723c-.57 12.48-2.655 19.257-4.408 23.768c-2.322 5.974-5.096 10.239-9.575 14.718s-8.743 7.253-14.717 9.574c-4.511 1.753-11.289 3.839-23.769 4.408c-13.495.616-17.543.746-51.722.746s-38.228-.13-51.723-.746c-12.48-.57-19.257-2.655-23.768-4.408c-5.974-2.321-10.239-5.095-14.718-9.574c-4.479-4.48-7.253-8.744-9.574-14.718c-1.753-4.51-3.839-11.288-4.408-23.768c-.616-13.497-.746-17.545-.746-51.723s.13-38.225.746-51.722c.57-12.48 2.655-19.258 4.408-23.769c2.321-5.974 5.095-10.238 9.574-14.717c4.48-4.48 8.744-7.253 14.718-9.575c4.51-1.753 11.288-3.838 23.768-4.408c13.497-.615 17.545-.745 51.723-.745M128 0C93.237 0 88.878.147 75.226.77c-13.625.622-22.93 2.786-31.071 5.95c-8.418 3.271-15.556 7.648-22.672 14.764S9.991 35.738 6.72 44.155C3.555 52.297 1.392 61.602.77 75.226C.147 88.878 0 93.237 0 128s.147 39.122.77 52.774c.622 13.625 2.785 22.93 5.95 31.071c3.27 8.417 7.647 15.556 14.763 22.672s14.254 11.492 22.672 14.763c8.142 3.165 17.446 5.328 31.07 5.95c13.653.623 18.012.77 52.775.77s39.122-.147 52.774-.77c13.624-.622 22.929-2.785 31.07-5.95c8.418-3.27 15.556-7.647 22.672-14.763s11.493-14.254 14.764-22.672c3.164-8.142 5.328-17.446 5.95-31.07c.623-13.653.77-18.012.77-52.775s-.147-39.122-.77-52.774c-.622-13.624-2.786-22.929-5.95-31.07c-3.271-8.418-7.648-15.556-14.764-22.672S220.262 9.99 211.845 6.72c-8.142-3.164-17.447-5.328-31.071-5.95C167.122.147 162.763 0 128 0m0 62.27c-36.302 0-65.73 29.43-65.73 65.73s29.428 65.73 65.73 65.73c36.301 0 65.73-29.428 65.73-65.73c0-36.301-29.429-65.73-65.73-65.73m0 108.397c-23.564 0-42.667-19.103-42.667-42.667S104.436 85.333 128 85.333s42.667 19.103 42.667 42.667s-19.103 42.667-42.667 42.667m83.686-110.994c0 8.484-6.876 15.36-15.36 15.36s-15.36-6.876-15.36-15.36s6.877-15.36 15.36-15.36s15.36 6.877 15.36 15.36"/></>) },
|
|
217
|
+
facebook: { viewBox: '0 0 256 256', path: (<><path fill="#1877f2" d="M256 128C256 57.308 198.692 0 128 0S0 57.308 0 128c0 63.888 46.808 116.843 108 126.445V165H75.5v-37H108V99.8c0-32.08 19.11-49.8 48.348-49.8C170.352 50 185 52.5 185 52.5V84h-16.14C152.959 84 148 93.867 148 103.99V128h35.5l-5.675 37H148v89.445c61.192-9.602 108-62.556 108-126.445"/><path fill="#fff" d="m177.825 165l5.675-37H148v-24.01C148 93.866 152.959 84 168.86 84H185V52.5S170.352 50 156.347 50C127.11 50 108 67.72 108 99.8V128H75.5v37H108v89.445A129 129 0 0 0 128 256a129 129 0 0 0 20-1.555V165z"/></>) },
|
|
218
|
+
meta: { viewBox: '0 0 256 171', path: (<><defs><linearGradient id="SVGYeLhubCc" x1="13.878%" x2="89.144%" y1="55.934%" y2="58.694%"><stop offset="0%" stopColor="#0064e1"/><stop offset="40%" stopColor="#0064e1"/><stop offset="83%" stopColor="#0073ee"/><stop offset="100%" stopColor="#0082fb"/></linearGradient><linearGradient id="SVGll66Sdsg" x1="54.315%" x2="54.315%" y1="82.782%" y2="39.307%"><stop offset="0%" stopColor="#0082fb"/><stop offset="100%" stopColor="#0064e0"/></linearGradient></defs><path fill="#0081fb" d="M27.651 112.136c0 9.775 2.146 17.28 4.95 21.82c3.677 5.947 9.16 8.466 14.751 8.466c7.211 0 13.808-1.79 26.52-19.372c10.185-14.092 22.186-33.874 30.26-46.275l13.675-21.01c9.499-14.591 20.493-30.811 33.1-41.806C161.196 4.985 172.298 0 183.47 0c18.758 0 36.625 10.87 50.3 31.257C248.735 53.584 256 81.707 256 110.729c0 17.253-3.4 29.93-9.187 39.946c-5.591 9.686-16.488 19.363-34.818 19.363v-27.616c15.695 0 19.612-14.422 19.612-30.927c0-23.52-5.484-49.623-17.564-68.273c-8.574-13.23-19.684-21.313-31.907-21.313c-13.22 0-23.859 9.97-35.815 27.75c-6.356 9.445-12.882 20.956-20.208 33.944l-8.066 14.289c-16.203 28.728-20.307 35.271-28.408 46.07c-14.2 18.91-26.324 26.076-42.287 26.076c-18.935 0-30.91-8.2-38.325-20.556C2.973 139.413 0 126.202 0 111.148z"/><path fill="url(#SVGYeLhubCc)" d="M21.802 33.206C34.48 13.666 52.774 0 73.757 0C85.91 0 97.99 3.597 110.605 13.897c13.798 11.261 28.505 29.805 46.853 60.368l6.58 10.967c15.881 26.459 24.917 40.07 30.205 46.49c6.802 8.243 11.565 10.7 17.752 10.7c15.695 0 19.612-14.422 19.612-30.927l24.393-.766c0 17.253-3.4 29.93-9.187 39.946c-5.591 9.686-16.488 19.363-34.818 19.363c-11.395 0-21.49-2.475-32.654-13.007c-8.582-8.083-18.615-22.443-26.334-35.352l-22.96-38.352C118.528 64.08 107.96 49.73 101.845 43.23c-6.578-6.988-15.036-15.428-28.532-15.428c-10.923 0-20.2 7.666-27.963 19.39z"/><path fill="url(#SVGll66Sdsg)" d="M73.312 27.802c-10.923 0-20.2 7.666-27.963 19.39c-10.976 16.568-17.698 41.245-17.698 64.944c0 9.775 2.146 17.28 4.95 21.82L9.027 149.482C2.973 139.413 0 126.202 0 111.148C0 83.772 7.514 55.24 21.802 33.206C34.48 13.666 52.774 0 73.757 0z"/></>) },
|
|
219
|
+
x: { viewBox: '0 0 251 256', path: (<><path d="M149.079 108.399L242.33 0h-22.098l-80.97 94.12L74.59 0H0l97.796 142.328L0 256h22.1l85.507-99.395L175.905 256h74.59L149.073 108.399zM118.81 143.58l-9.909-14.172l-78.84-112.773h33.943l63.625 91.011l9.909 14.173l82.705 118.3H186.3l-67.49-96.533z"/></>) },
|
|
220
|
+
linkedin: { viewBox: '0 0 256 256', path: (<><path fill="#0a66c2" d="M218.123 218.127h-37.931v-59.403c0-14.165-.253-32.4-19.728-32.4c-19.756 0-22.779 15.434-22.779 31.369v60.43h-37.93V95.967h36.413v16.694h.51a39.91 39.91 0 0 1 35.928-19.733c38.445 0 45.533 25.288 45.533 58.186zM56.955 79.27c-12.157.002-22.014-9.852-22.016-22.009s9.851-22.014 22.008-22.016c12.157-.003 22.014 9.851 22.016 22.008A22.013 22.013 0 0 1 56.955 79.27m18.966 138.858H37.95V95.967h37.97zM237.033.018H18.89C8.58-.098.125 8.161-.001 18.471v219.053c.122 10.315 8.576 18.582 18.89 18.474h218.144c10.336.128 18.823-8.139 18.966-18.474V18.454c-.147-10.33-8.635-18.588-18.966-18.453"/></>) },
|
|
221
|
+
youtube: { viewBox: '0 0 256 180', path: (<><path fill="red" d="M250.346 28.075A32.18 32.18 0 0 0 227.69 5.418C207.824 0 127.87 0 127.87 0S47.912.164 28.046 5.582A32.18 32.18 0 0 0 5.39 28.24c-6.009 35.298-8.34 89.084.165 122.97a32.18 32.18 0 0 0 22.656 22.657c19.866 5.418 99.822 5.418 99.822 5.418s79.955 0 99.82-5.418a32.18 32.18 0 0 0 22.657-22.657c6.338-35.348 8.291-89.1-.164-123.134"/><path fill="#fff" d="m102.421 128.06l66.328-38.418l-66.328-38.418z"/></>) },
|
|
222
|
+
pinterest: { viewBox: '0 0 256 256', path: (<><path fill="#cb1f27" d="M0 128.002c0 52.414 31.518 97.442 76.619 117.239c-.36-8.938-.064-19.668 2.228-29.393c2.461-10.391 16.47-69.748 16.47-69.748s-4.089-8.173-4.089-20.252c0-18.969 10.994-33.136 24.686-33.136c11.643 0 17.268 8.745 17.268 19.217c0 11.704-7.465 29.211-11.304 45.426c-3.207 13.578 6.808 24.653 20.203 24.653c24.252 0 40.586-31.149 40.586-68.055c0-28.054-18.895-49.052-53.262-49.052c-38.828 0-63.017 28.956-63.017 61.3c0 11.152 3.288 19.016 8.438 25.106c2.368 2.797 2.697 3.922 1.84 7.134c-.614 2.355-2.024 8.025-2.608 10.272c-.852 3.242-3.479 4.401-6.409 3.204c-17.884-7.301-26.213-26.886-26.213-48.902c0-36.361 30.666-79.961 91.482-79.961c48.87 0 81.035 35.364 81.035 73.325c0 50.213-27.916 87.726-69.066 87.726c-13.819 0-26.818-7.47-31.271-15.955c0 0-7.431 29.492-9.005 35.187c-2.714 9.869-8.026 19.733-12.883 27.421a127.9 127.9 0 0 0 36.277 5.249c70.684 0 127.996-57.309 127.996-128.005C256.001 57.309 198.689 0 128.005 0C57.314 0 0 57.309 0 128.002"/></>) },
|
|
223
|
+
tiktok: { viewBox: '0 0 256 290', path: (<><path fill="#ff004f" d="M189.72 104.421c18.678 13.345 41.56 21.197 66.273 21.197v-47.53a67 67 0 0 1-13.918-1.456v37.413c-24.711 0-47.59-7.851-66.272-21.195v96.996c0 48.523-39.356 87.855-87.9 87.855c-18.113 0-34.949-5.473-48.934-14.86c15.962 16.313 38.222 26.432 62.848 26.432c48.548 0 87.905-39.332 87.905-87.857v-96.995zm17.17-47.952c-9.546-10.423-15.814-23.893-17.17-38.785v-6.113h-13.189c3.32 18.927 14.644 35.097 30.358 44.898M69.673 225.607a40 40 0 0 1-8.203-24.33c0-22.192 18.001-40.186 40.21-40.186a40.3 40.3 0 0 1 12.197 1.883v-48.593c-4.61-.631-9.262-.9-13.912-.801v37.822a40.3 40.3 0 0 0-12.203-1.882c-22.208 0-40.208 17.992-40.208 40.187c0 15.694 8.997 29.281 22.119 35.9"/><path d="M175.803 92.849c18.683 13.344 41.56 21.195 66.272 21.195V76.631c-13.794-2.937-26.005-10.141-35.186-20.162c-15.715-9.802-27.038-25.972-30.358-44.898h-34.643v189.843c-.079 22.132-18.049 40.052-40.21 40.052c-13.058 0-24.66-6.221-32.007-15.86c-13.12-6.618-22.118-20.206-22.118-35.898c0-22.193 18-40.187 40.208-40.187c4.255 0 8.356.662 12.203 1.882v-37.822c-47.692.985-86.047 39.933-86.047 87.834c0 23.912 9.551 45.589 25.053 61.428c13.985 9.385 30.82 14.86 48.934 14.86c48.545 0 87.9-39.335 87.9-87.857z"/><path fill="#00f2ea" d="M242.075 76.63V66.516a66.3 66.3 0 0 1-35.186-10.047a66.47 66.47 0 0 0 35.186 20.163M176.53 11.57a68 68 0 0 1-.728-5.457V0h-47.834v189.845c-.076 22.13-18.046 40.05-40.208 40.05a40.06 40.06 0 0 1-18.09-4.287c7.347 9.637 18.949 15.857 32.007 15.857c22.16 0 40.132-17.918 40.21-40.05V11.571zM99.966 113.58v-10.769a89 89 0 0 0-12.061-.818C39.355 101.993 0 141.327 0 189.845c0 30.419 15.467 57.227 38.971 72.996c-15.502-15.838-25.053-37.516-25.053-61.427c0-47.9 38.354-86.848 86.048-87.833"/></>) },
|
|
224
|
+
snapchat: { viewBox: '147.353 39.286 514.631 514.631', path: (<><path style={{ fill: '#FFFC00' }} d="M147.553,423.021v0.023c0.308,11.424,0.403,22.914,2.33,34.268 c2.042,12.012,4.961,23.725,10.53,34.627c7.529,14.756,17.869,27.217,30.921,37.396c9.371,7.309,19.608,13.111,30.94,16.771 c16.524,5.33,33.571,7.373,50.867,7.473c10.791,0.068,21.575,0.338,32.37,0.293c78.395-0.33,156.792,0.566,235.189-0.484 c10.403-0.141,20.636-1.41,30.846-3.277c19.569-3.582,36.864-11.932,51.661-25.133c17.245-15.381,28.88-34.205,34.132-56.924 c3.437-14.85,4.297-29.916,4.444-45.035v-3.016c0-1.17-0.445-256.892-0.486-260.272c-0.115-9.285-0.799-18.5-2.54-27.636 c-2.117-11.133-5.108-21.981-10.439-32.053c-5.629-10.641-12.68-20.209-21.401-28.57c-13.359-12.81-28.775-21.869-46.722-26.661 c-16.21-4.327-32.747-5.285-49.405-5.27c-0.027-0.004-0.09-0.173-0.094-0.255H278.56c-0.005,0.086-0.008,0.172-0.014,0.255 c-9.454,0.173-18.922,0.102-28.328,1.268c-10.304,1.281-20.509,3.21-30.262,6.812c-15.362,5.682-28.709,14.532-40.11,26.347 c-12.917,13.386-22.022,28.867-26.853,46.894c-4.31,16.084-5.248,32.488-5.271,49.008"> </path> <path style={{ fill: '#FFFFFF' }} d="M407.001,473.488c-1.068,0-2.087-0.039-2.862-0.076c-0.615,0.053-1.25,0.076-1.886,0.076 c-22.437,0-37.439-10.607-50.678-19.973c-9.489-6.703-18.438-13.031-28.922-14.775c-5.149-0.854-10.271-1.287-15.22-1.287 c-8.917,0-15.964,1.383-21.109,2.389c-3.166,0.617-5.896,1.148-8.006,1.148c-2.21,0-4.895-0.49-6.014-4.311 c-0.887-3.014-1.523-5.934-2.137-8.746c-1.536-7.027-2.65-11.316-5.281-11.723c-28.141-4.342-44.768-10.738-48.08-18.484 c-0.347-0.814-0.541-1.633-0.584-2.443c-0.129-2.309,1.501-4.334,3.777-4.711c22.348-3.68,42.219-15.492,59.064-35.119 c13.049-15.195,19.457-29.713,20.145-31.316c0.03-0.072,0.065-0.148,0.101-0.217c3.247-6.588,3.893-12.281,1.926-16.916 c-3.626-8.551-15.635-12.361-23.58-14.882c-1.976-0.625-3.845-1.217-5.334-1.808c-7.043-2.782-18.626-8.66-17.083-16.773 c1.124-5.916,8.949-10.036,15.273-10.036c1.756,0,3.312,0.308,4.622,0.923c7.146,3.348,13.575,5.045,19.104,5.045 c6.876,0,10.197-2.618,11-3.362c-0.198-3.668-0.44-7.546-0.674-11.214c0-0.004-0.005-0.048-0.005-0.048 c-1.614-25.675-3.627-57.627,4.546-75.95c24.462-54.847,76.339-59.112,91.651-59.112c0.408,0,6.674-0.062,6.674-0.062 c0.283-0.005,0.59-0.009,0.908-0.009c15.354,0,67.339,4.27,91.816,59.15c8.173,18.335,6.158,50.314,4.539,76.016l-0.076,1.23 c-0.222,3.49-0.427,6.793-0.6,9.995c0.756,0.696,3.795,3.096,9.978,3.339c5.271-0.202,11.328-1.891,17.998-5.014 c2.062-0.968,4.345-1.169,5.895-1.169c2.343,0,4.727,0.456,6.714,1.285l0.106,0.041c5.66,2.009,9.367,6.024,9.447,10.242 c0.071,3.932-2.851,9.809-17.223,15.485c-1.472,0.583-3.35,1.179-5.334,1.808c-7.952,2.524-19.951,6.332-23.577,14.878 c-1.97,4.635-1.322,10.326,1.926,16.912c0.036,0.072,0.067,0.145,0.102,0.221c1,2.344,25.205,57.535,79.209,66.432 c2.275,0.379,3.908,2.406,3.778,4.711c-0.048,0.828-0.248,1.656-0.598,2.465c-3.289,7.703-19.915,14.09-48.064,18.438 c-2.642,0.408-3.755,4.678-5.277,11.668c-0.63,2.887-1.271,5.717-2.146,8.691c-0.819,2.797-2.641,4.164-5.567,4.164h-0.441 c-1.905,0-4.604-0.346-8.008-1.012c-5.95-1.158-12.623-2.236-21.109-2.236c-4.948,0-10.069,0.434-15.224,1.287 c-10.473,1.744-19.421,8.062-28.893,14.758C444.443,462.88,429.436,473.488,407.001,473.488"> </path> <path style={{ fill: '#020202' }} d="M408.336,124.235c14.455,0,64.231,3.883,87.688,56.472c7.724,17.317,5.744,48.686,4.156,73.885 c-0.248,3.999-0.494,7.875-0.694,11.576l-0.084,1.591l1.062,1.185c0.429,0.476,4.444,4.672,13.374,5.017l0.144,0.008l0.15-0.003 c5.904-0.225,12.554-2.059,19.776-5.442c1.064-0.498,2.48-0.741,3.978-0.741c1.707,0,3.521,0.321,5.017,0.951l0.226,0.09 c3.787,1.327,6.464,3.829,6.505,6.093c0.022,1.28-0.935,5.891-14.359,11.194c-1.312,0.518-3.039,1.069-5.041,1.7 c-8.736,2.774-21.934,6.96-26.376,17.427c-2.501,5.896-1.816,12.854,2.034,20.678c1.584,3.697,26.52,59.865,82.631,69.111 c-0.011,0.266-0.079,0.557-0.229,0.9c-0.951,2.24-6.996,9.979-44.612,15.783c-5.886,0.902-7.328,7.5-9,15.17 c-0.604,2.746-1.218,5.518-2.062,8.381c-0.258,0.865-0.306,0.914-1.233,0.914c-0.128,0-0.278,0-0.442,0 c-1.668,0-4.2-0.346-7.135-0.922c-5.345-1.041-12.647-2.318-21.982-2.318c-5.21,0-10.577,0.453-15.962,1.352 c-11.511,1.914-20.872,8.535-30.786,15.543c-13.314,9.408-27.075,19.143-48.071,19.143c-0.917,0-1.812-0.031-2.709-0.076 l-0.236-0.01l-0.237,0.018c-0.515,0.045-1.034,0.068-1.564,0.068c-20.993,0-34.76-9.732-48.068-19.143 c-9.916-7.008-19.282-13.629-30.791-15.543c-5.38-0.896-10.752-1.352-15.959-1.352c-9.333,0-16.644,1.428-21.978,2.471 c-2.935,0.574-5.476,1.066-7.139,1.066c-1.362,0-1.388-0.08-1.676-1.064c-0.844-2.865-1.461-5.703-2.062-8.445 c-1.676-7.678-3.119-14.312-9.002-15.215c-37.613-5.809-43.659-13.561-44.613-15.795c-0.149-0.352-0.216-0.652-0.231-0.918 c56.11-9.238,81.041-65.408,82.63-69.119c3.857-7.818,4.541-14.775,2.032-20.678c-4.442-10.461-17.638-14.653-26.368-17.422 c-2.007-0.635-3.735-1.187-5.048-1.705c-11.336-4.479-14.823-8.991-14.305-11.725c0.601-3.153,6.067-6.359,10.837-6.359 c1.072,0,2.012,0.173,2.707,0.498c7.747,3.631,14.819,5.472,21.022,5.472c9.751,0,14.091-4.537,14.557-5.055l1.057-1.182 l-0.085-1.583c-0.197-3.699-0.44-7.574-0.696-11.565c-1.583-25.205-3.563-56.553,4.158-73.871 c23.37-52.396,72.903-56.435,87.525-56.435c0.36,0,6.717-0.065,6.717-0.065C407.744,124.239,408.033,124.235,408.336,124.235 M408.336,115.197h-0.017c-0.333,0-0.646,0-0.944,0.004c-2.376,0.024-6.282,0.062-6.633,0.066c-8.566,0-25.705,1.21-44.115,9.336 c-10.526,4.643-19.994,10.921-28.14,18.66c-9.712,9.221-17.624,20.59-23.512,33.796c-8.623,19.336-6.576,51.905-4.932,78.078 l0.006,0.041c0.176,2.803,0.361,5.73,0.53,8.582c-1.265,0.581-3.316,1.194-6.339,1.194c-4.864,0-10.648-1.555-17.187-4.619 c-1.924-0.896-4.12-1.349-6.543-1.349c-3.893,0-7.997,1.146-11.557,3.239c-4.479,2.63-7.373,6.347-8.159,10.468 c-0.518,2.726-0.493,8.114,5.492,13.578c3.292,3.008,8.128,5.782,14.37,8.249c1.638,0.645,3.582,1.261,5.641,1.914 c7.145,2.271,17.959,5.702,20.779,12.339c1.429,3.365,0.814,7.793-1.823,13.145c-0.069,0.146-0.138,0.289-0.201,0.439 c-0.659,1.539-6.807,15.465-19.418,30.152c-7.166,8.352-15.059,15.332-23.447,20.752c-10.238,6.617-21.316,10.943-32.923,12.855 c-4.558,0.748-7.813,4.809-7.559,9.424c0.078,1.33,0.39,2.656,0.931,3.939c0.004,0.008,0.009,0.016,0.013,0.023 c1.843,4.311,6.116,7.973,13.063,11.203c8.489,3.943,21.185,7.26,37.732,9.855c0.836,1.59,1.704,5.586,2.305,8.322 c0.629,2.908,1.285,5.898,2.22,9.074c1.009,3.441,3.626,7.553,10.349,7.553c2.548,0,5.478-0.574,8.871-1.232 c4.969-0.975,11.764-2.305,20.245-2.305c4.702,0,9.575,0.414,14.48,1.229c9.455,1.574,17.606,7.332,27.037,14 c13.804,9.758,29.429,20.803,53.302,20.803c0.651,0,1.304-0.021,1.949-0.066c0.789,0.037,1.767,0.066,2.799,0.066 c23.88,0,39.501-11.049,53.29-20.799l0.022-0.02c9.433-6.66,17.575-12.41,27.027-13.984c4.903-0.814,9.775-1.229,14.479-1.229 c8.102,0,14.517,1.033,20.245,2.15c3.738,0.736,6.643,1.09,8.872,1.09l0.218,0.004h0.226c4.917,0,8.53-2.699,9.909-7.422 c0.916-3.109,1.57-6.029,2.215-8.986c0.562-2.564,1.46-6.674,2.296-8.281c16.558-2.6,29.249-5.91,37.739-9.852 c6.931-3.215,11.199-6.873,13.053-11.166c0.556-1.287,0.881-2.621,0.954-3.979c0.261-4.607-2.999-8.676-7.56-9.424 c-51.585-8.502-74.824-61.506-75.785-63.758c-0.062-0.148-0.132-0.295-0.205-0.438c-2.637-5.354-3.246-9.777-1.816-13.148 c2.814-6.631,13.621-10.062,20.771-12.332c2.07-0.652,4.021-1.272,5.646-1.914c7.039-2.78,12.07-5.796,15.389-9.221 c3.964-4.083,4.736-7.995,4.688-10.555c-0.121-6.194-4.856-11.698-12.388-14.393c-2.544-1.052-5.445-1.607-8.399-1.607 c-2.011,0-4.989,0.276-7.808,1.592c-6.035,2.824-11.441,4.368-16.082,4.588c-2.468-0.125-4.199-0.66-5.32-1.171 c0.141-2.416,0.297-4.898,0.458-7.486l0.067-1.108c1.653-26.19,3.707-58.784-4.92-78.134c-5.913-13.253-13.853-24.651-23.604-33.892 c-8.178-7.744-17.678-14.021-28.242-18.661C434.052,116.402,416.914,115.197,408.336,115.197"> </path> <rect x="147.553" y="39.443" style={{ fill: 'none' }} width="514.231" height="514.23"> </rect></>) },
|
|
225
|
+
google: { viewBox: '0 0 256 262', path: (<><path fill="#4285f4" d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622l38.755 30.023l2.685.268c24.659-22.774 38.875-56.282 38.875-96.027"/><path fill="#34a853" d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055c-34.523 0-63.824-22.773-74.269-54.25l-1.531.13l-40.298 31.187l-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1"/><path fill="#fbbc05" d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82c0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602z"/><path fill="#eb4335" d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0C79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251"/></>) },
|
|
226
|
+
amazon: { viewBox: '0 0 512 512', path: (<><path d="m0 0H512V512H0" fill="#f90"/><path fill="#fff" d="M283 187c-62 2-121 19-121 81 0 43 26 64 61 64 31 0 51-12 68-30 8 11 10 16 24 27q5 3 10-1l31-27q4-4 0-10c-7-11-15-19-15-39v-64c0-27 2-52-18-70-17-16-38-20-62-21-53-1-88 28-93 62q-1 8 7 9l37 5c6 1 9-4 10-8 6-22 29-28 43-23 20 6 18 29 18 45m-36 105c-15 0-25-13-25-30 1-36 29-42 61-42v18c0 32-17 54-36 54m168 106c13-11 26-38 25-57 0-7-1-8-8-10-13-4-46-5-62 10-3 3-2 5 1 5 11-2 45-6 50 2 4 7-8 35-12 47-2 5 2 6 6 3M58 342c96 91 247 94 345 25 7-4 0-12-6-9A376 376 0 0162 337c-4-3-8 2-4 5"/></>) },
|
|
227
|
+
walmart: { viewBox: '-231.5 619.2 64 64', path: (<><path d="M-199.47 640.732c1.488 0 2.778-.794 2.878-1.786l1.488-16.77c0-1.687-1.985-2.977-4.366-2.977-2.48 0-4.366 1.4-4.366 2.977l1.488 16.77c.1.992 1.4 1.786 2.878 1.786M-208.6 646c.794-1.3.695-2.778-.1-3.374L-222.492 633c-1.4-.794-3.572.198-4.763 2.282s-.992 4.465.397 5.26l15.28 7.144c.992.298 2.282-.397 2.977-1.687m18.16 0c.794 1.3 2.084 1.984 2.977 1.588l15.28-7.144c1.4-.794 1.687-3.175.397-5.26-1.2-2.084-3.374-3.076-4.763-2.282l-13.792 9.625c-.794.695-.893 2.183-.1 3.473m-9.027 15.678c1.488 0 2.778.794 2.878 1.786l1.488 16.77c0 1.687-1.985 2.977-4.366 2.977-2.48 0-4.366-1.4-4.366-2.977l1.488-16.77c.1-.992 1.4-1.786 2.878-1.786m9.027-5.277c.794-1.3 2.084-1.985 2.977-1.588l15.28 7.045c1.4.794 1.687 3.175.397 5.26-1.2 2.084-3.374 3.076-4.763 2.282l-13.792-9.625c-.794-.595-.893-2.084-.1-3.374m-18.157 0c.794 1.3.695 2.778-.1 3.374l-13.792 9.625c-1.4.794-3.572-.198-4.763-2.282s-.992-4.465.397-5.26l15.28-7.045c.992-.397 2.282.298 2.977 1.588"/></>) },
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Every platform `<BrandIcon>` can draw in colour. */
|
|
231
|
+
export function hasBrandIcon(name: string): boolean {
|
|
232
|
+
return name in COLOR_MARKS
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export interface BrandIconRegistryProps extends BrandIconProps {
|
|
236
|
+
/** Platform key, e.g. "facebook", "youtube". */
|
|
237
|
+
name: string
|
|
238
|
+
/** Give a label only when the mark stands alone; decorative beside text. */
|
|
239
|
+
title?: string | undefined
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* A full-colour platform mark, looked up by key. Use when the platform is
|
|
244
|
+
* chosen at runtime; use the named mono `Brand*` components when it is known
|
|
245
|
+
* at author time and the mark sits in coloured chrome.
|
|
246
|
+
*/
|
|
247
|
+
export function BrandIcon({ name, size = 16, className, title }: BrandIconRegistryProps) {
|
|
248
|
+
const mark = COLOR_MARKS[name]
|
|
249
|
+
if (!mark) return null
|
|
250
|
+
return (
|
|
251
|
+
<svg
|
|
252
|
+
viewBox={mark.viewBox}
|
|
253
|
+
width={size}
|
|
254
|
+
height={size}
|
|
255
|
+
className={className}
|
|
256
|
+
// Meta is 256x171 and YouTube 256x180 — assuming square would stretch them.
|
|
257
|
+
preserveAspectRatio="xMidYMid meet"
|
|
258
|
+
role={title ? 'img' : undefined}
|
|
259
|
+
aria-hidden={title ? undefined : true}
|
|
260
|
+
focusable="false"
|
|
261
|
+
>
|
|
262
|
+
{title && <title>{title}</title>}
|
|
263
|
+
{mark.path}
|
|
264
|
+
</svg>
|
|
265
|
+
)
|
|
266
|
+
}
|