// Voltro brand mark — the company + framework logo. // // The mark is a faceted lightning bolt ("volt") that also reads as the // "V" of Voltro: two folded facets catch light off a central crease, so // it has depth at billboard size yet stays legible as a 16px favicon. // On-brand violet (oklch(0.55 0.25 290) ≈ #7C3AED — the same hue the // text-gradient + mesh backdrop use). // // ▲ VoltroMark — the bolt alone. variant: gradient | mono | white. // ◳ VoltroIcon — app/ad icon: bolt on a rounded "squircle". // ▦ VoltroWordmark — mark + "Voltro" set in the current font. // // Every app consumes ONE source: `import { VoltroMark } from // '@voltro/ui-shadcn'`. `mono` paints in `currentColor`, so it inherits // the surrounding text colour and is automatically correct in light AND // dark mode. The gradient + icon variants carry their own colour and // work on any background. For static assets (favicon, OG image, app-store // icon) use the sibling `assets/*.svg` files. import type { CSSProperties, ReactNode, SVGProps } from 'react' import { useId } from 'react' import { cn } from '../cn' // The bolt geometry, authored once in a 48×48 box and reused everywhere. // Sharp joins read as "electric"; the facets share the crease T→C. const BOLT_FULL = 'M28 5 13 27 22 26 20 43 35 21 26 22Z' // outer silhouette (one fill) const BOLT_FRONT = 'M28 5 13 27 22 26 20 43Z' // left / lit facet const BOLT_BACK = 'M28 5 20 43 35 21 26 22Z' // right / shaded facet /** Brand violet — the canonical primary. Exposed for ad-hoc usage. */ export const VOLTRO_VIOLET = '#7C3AED' export type VoltroMarkVariant = 'gradient' | 'mono' | 'white' // The component owns the structural attrs (viewBox / fill / xmlns / children) and // the a11y trio (role / aria-label / aria-hidden, driven by `title`), so they are // excluded from the pass-through: leaving them in would let the last-spread `rest` // clobber the component's own literals with `T | undefined`, which trips a strict // deployment's `exactOptionalPropertyTypes` (the mark typechecks in its own project but // broke when a downstream app compiled this source under that flag). // // `ref` is excluded too, and for a different reason: this is a plain function // component (not `forwardRef`), so it never wires a ref anywhere — the prop was // never honoured. Keeping it in the pass-through also spreads `SVGProps['ref']` // onto the ``; when a downstream app compiles THIS source, that `ref` type // resolves against the framework's `@types/react` copy while the intrinsic `` // resolves against the app's own copy. Same version, but two structurally-distinct // `Ref`/`VoidOrUndefinedOnly` types → TS reports them as "unrelated" and the spread // fails. Dropping `ref` from the surface removes the dead prop and the clash at once. export interface VoltroMarkProps extends Omit< SVGProps, 'width' | 'height' | 'viewBox' | 'fill' | 'xmlns' | 'children' | 'ref' | 'role' | 'aria-label' | 'aria-hidden' > { /** Pixel size (square). Default 32. Pass a string like '1em' to track font-size. */ readonly size?: number | string /** `gradient` (default, two-facet violet) · `mono` (currentColor) · `white`. */ readonly variant?: VoltroMarkVariant /** Accessible label. Omit + set `aria-hidden` when paired with a text label. */ readonly title?: string } /** The bolt mark on a transparent background. */ export const VoltroMark = ({ size = 32, variant = 'gradient', title, className, ...rest }: VoltroMarkProps): ReactNode => { const uid = useId() const front = `vmF-${uid}` const back = `vmB-${uid}` const labelled = title !== undefined // One non-union type: explicit `role={... : undefined}` props violate // exactOptionalPropertyTypes, and a conditional-spread union chokes older // checkers in cross-repo consumers. const a11y: Pick, 'role' | 'aria-label' | 'aria-hidden'> = labelled ? { role: 'img', 'aria-label': title } : { 'aria-hidden': true } return ( {variant === 'gradient' ? ( <> ) : ( )} ) } export type VoltroIconTone = 'brand' | 'dark' export interface VoltroIconProps { /** Pixel size (square). Default 40. */ readonly size?: number /** `brand` (violet squircle + white bolt — works on any surface) · `dark` (near-black squircle + glowing violet bolt). */ readonly tone?: VoltroIconTone /** Corner radius as a fraction of size. Default 0.225 (iOS-style squircle look). */ readonly radius?: number readonly title?: string readonly className?: string readonly style?: CSSProperties } /** * The app / advertising icon: the bolt centred on a rounded square with * its own background, so it stands on a home screen, store listing, or * ad placement regardless of the surface behind it. */ export const VoltroIcon = ({ size = 40, tone = 'brand', radius = 0.225, title = 'Voltro', className, style, }: VoltroIconProps): ReactNode => { const uid = useId() const bg = `viBg-${uid}` const glow = `viGlow-${uid}` const front = `viF-${uid}` const back = `viB-${uid}` const rx = (64 * radius).toFixed(2) return ( {tone === 'brand' ? ( <> ) : ( <> )} {tone === 'dark' ? ( ) : null} {tone === 'brand' ? ( // Two-tone bolt — white front facet + pale-violet back facet keep the // folded-light identity readable on the violet field (not a flat glyph). <> ) : ( <> )} ) } export interface VoltroWordmarkProps { /** Mark height in px; the wordmark text scales with it. Default 28. */ readonly size?: number readonly markVariant?: VoltroMarkVariant /** Wordmark label. Default "Voltro". */ readonly label?: string readonly className?: string readonly style?: CSSProperties } /** * Mark + wordmark lock-up for headers / marketing. The text is real * type set in the surrounding font and `currentColor`, so it inherits * the app's typography and is correct in light + dark automatically. */ export const VoltroWordmark = ({ size = 28, markVariant = 'gradient', label = 'Voltro', className, style, }: VoltroWordmarkProps): ReactNode => ( {/* Mark rendered larger than the cap height so the bolt reads as the lead element, not a small adornment beside the wordmark. */} {label} ) /** Raw bolt path data, for apps that need to compose their own SVG. */ export const VOLTRO_BOLT_PATH = { full: BOLT_FULL, front: BOLT_FRONT, back: BOLT_BACK } as const