@orangecheck/app-design 0.1.0 → 0.1.1

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/brand.js CHANGED
@@ -97,13 +97,15 @@ var SPHERE_GRADIENT = "radial-gradient(circle at 50% 42%, #fff8f3 0%, #ffe2d3 22
97
97
  function HalftoneSphere({ src, alt, stadium = false, float = false, className, style }) {
98
98
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative aspect-square w-full", className), style, children: [
99
99
  stadium ? (
100
- // The deck's offset stadium on wide screens; centred and inside the
101
- // gutter on phones, where the offset would push the page sideways.
100
+ // Measured from the deck's title slide: a rounded square, its corners
101
+ // 17% of the sphere, level with the sphere's foot, reaching left behind
102
+ // the headline. On phones a ring inside the gutter, since the offset
103
+ // would push the page sideways.
102
104
  /* @__PURE__ */ jsxRuntime.jsx(
103
105
  "div",
104
106
  {
105
107
  "aria-hidden": true,
106
- className: "oca-stadium pointer-events-none absolute inset-[-6%] md:[inset:-13%_10%_-13%_-27%]"
108
+ className: "oca-stadium pointer-events-none absolute inset-[-6%] rounded-full md:[inset:-12%_14%_0%_-26%] md:rounded-[17%]"
107
109
  }
108
110
  )
109
111
  ) : null,
package/dist/brand.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/cn.ts","../src/brand/mark.tsx","../src/brand/sphere.tsx","../src/brand/iso-check.tsx"],"names":["extendTailwindMerge","clsx","jsxs","jsx"],"mappings":";;;;;;AAKA,IAAM,QAAQA,iCAAA,CAAgC;AAAA,EAC1C,MAAA,EAAQ;AAAA,IACJ,WAAA,EAAa;AAAA,MACT,UAAA,EAAY;AAAA,QACR,eAAA;AAAA,QACA,eAAA;AAAA,QACA,eAAA;AAAA,QACA,WAAA;AAAA,QACA,cAAA;AAAA,QACA,aAAA;AAAA,QACA,UAAA;AAAA,QACA,UAAA;AAAA,QACA;AAAA;AACJ;AACJ;AAER,CAAC,CAAA;AAEM,SAAS,MAAM,MAAA,EAA8B;AAChD,EAAA,OAAO,KAAA,CAAMC,SAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACfA,IAAM,GAAA,GAAM,2BAAA;AACZ,IAAM,IAAA,GAAO,iBAAA;AACb,IAAM,KAAA,GAAQ,mBAAA;AAEP,IAAM,aAAa,EAAE,GAAA,EAAK,WAAW,IAAA,EAAM,SAAA,EAAW,OAAO,SAAA;AAS7D,SAAS,KAAK,EAAE,KAAA,EAAO,MAAM,SAAA,EAAW,GAAG,MAAK,EAAc;AACjE,EAAA,uBACIC,eAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACG,OAAA,EAAQ,UAAA;AAAA,MACR,KAAA,EAAO,IAAA;AAAA,MACP,MAAA,EAAQ,IAAA,GAAO,IAAA,GAAO,CAAA,GAAI,MAAA;AAAA,MAC1B,IAAA,EAAM,QAAQ,KAAA,GAAQ,MAAA;AAAA,MACtB,aAAA,EAAa,QAAQ,MAAA,GAAY,IAAA;AAAA,MACjC,SAAA,EAAW,EAAA,CAAG,UAAA,EAAY,SAAS,CAAA;AAAA,MAClC,GAAG,IAAA;AAAA,MAEH,QAAA,EAAA;AAAA,QAAA,KAAA,mBAAQC,cAAA,CAAC,OAAA,EAAA,EAAO,QAAA,EAAA,KAAA,EAAM,CAAA,GAAW,IAAA;AAAA,uCACjC,SAAA,EAAA,EAAQ,MAAA,EAAQ,IAAA,EAAM,IAAA,EAAM,WAAW,IAAA,EAAM,CAAA;AAAA,uCAC7C,SAAA,EAAA,EAAQ,MAAA,EAAQ,KAAA,EAAO,IAAA,EAAM,WAAW,KAAA,EAAO,CAAA;AAAA,uCAC/C,SAAA,EAAA,EAAQ,MAAA,EAAQ,GAAA,EAAK,IAAA,EAAM,WAAW,GAAA,EAAK;AAAA;AAAA;AAAA,GAChD;AAER;AAQO,SAAS,OAAA,CAAQ,EAAE,KAAA,EAAO,IAAA,GAAO,IAAI,SAAA,EAAW,GAAG,MAAK,EAAiB;AAC5E,EAAA,uBACID,eAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACG,OAAA,EAAQ,WAAA;AAAA,MACR,KAAA,EAAO,IAAA;AAAA,MACP,MAAA,EAAQ,IAAA;AAAA,MACR,IAAA,EAAM,QAAQ,KAAA,GAAQ,MAAA;AAAA,MACtB,aAAA,EAAa,QAAQ,MAAA,GAAY,IAAA;AAAA,MACjC,SAAA,EAAW,EAAA,CAAG,UAAA,EAAY,SAAS,CAAA;AAAA,MAClC,GAAG,IAAA;AAAA,MAEH,QAAA,EAAA;AAAA,QAAA,KAAA,mBAAQC,cAAA,CAAC,OAAA,EAAA,EAAO,QAAA,EAAA,KAAA,EAAM,CAAA,GAAW,IAAA;AAAA,wBAClCA,cAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACG,KAAA,EAAM,IAAA;AAAA,YACN,MAAA,EAAO,IAAA;AAAA,YACP,EAAA,EAAG,MAAA;AAAA,YACH,KAAA,EAAO,EAAE,IAAA,EAAM,+BAAA,EAAiC,QAAQ,mCAAA,EAAoC;AAAA,YAC5F,WAAA,EAAY;AAAA;AAAA,SAChB;AAAA,wBACAD,eAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,iCAAA,EACT,QAAA,EAAA;AAAA,0BAAAC,cAAA,CAAC,SAAA,EAAA,EAAQ,MAAA,EAAQ,IAAA,EAAM,IAAA,EAAM,WAAW,IAAA,EAAM,CAAA;AAAA,yCAC7C,SAAA,EAAA,EAAQ,MAAA,EAAQ,KAAA,EAAO,IAAA,EAAM,WAAW,KAAA,EAAO,CAAA;AAAA,yCAC/C,SAAA,EAAA,EAAQ,MAAA,EAAQ,GAAA,EAAK,IAAA,EAAM,WAAW,GAAA,EAAK;AAAA,SAAA,EAChD;AAAA;AAAA;AAAA,GACJ;AAER;AAWO,SAAS,OAAO,EAAE,IAAA,GAAO,eAAe,IAAA,GAAO,EAAA,EAAI,WAAU,EAAgB;AAChF,EAAA,uBACID,eAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACG,SAAA,EAAW,EAAA,CAAG,yDAAA,EAA2D,SAAS,CAAA;AAAA,MAClF,OAAO,EAAE,GAAA,EAAK,OAAO,IAAA,EAAM,QAAA,EAAU,OAAO,IAAA,EAAK;AAAA,MAEjD,QAAA,EAAA;AAAA,wBAAAC,cAAA,CAAC,WAAQ,IAAA,EAAY,CAAA;AAAA,wBACrBA,cAAA,CAAC,UAAM,QAAA,EAAA,IAAA,EAAK;AAAA;AAAA;AAAA,GAChB;AAER;ACnFO,IAAM,eAAA,GACT;AAeG,SAAS,cAAA,CAAe,EAAE,GAAA,EAAK,GAAA,EAAK,OAAA,GAAU,OAAO,KAAA,GAAQ,KAAA,EAAO,SAAA,EAAW,KAAA,EAAM,EAAwB;AAChH,EAAA,uBACID,gBAAC,KAAA,EAAA,EAAI,SAAA,EAAW,GAAG,+BAAA,EAAiC,SAAS,GAAG,KAAA,EAC3D,QAAA,EAAA;AAAA,IAAA,OAAA;AAAA;AAAA;AAAA,sBAGGC,cAAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACG,aAAA,EAAW,IAAA;AAAA,UACX,SAAA,EAAU;AAAA;AAAA;AACd,QACA,IAAA;AAAA,oBACJA,cAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACG,SAAA,EAAW,EAAA,CAAG,iDAAA,EAAmD,KAAA,IAAS,mBAAmB,CAAA;AAAA,QAC7F,KAAA,EAAO,EAAE,UAAA,EAAY,eAAA,EAAgB;AAAA,QAGrC,QAAA,kBAAAA,eAAC,KAAA,EAAA,EAAI,GAAA,EAAU,KAAU,SAAA,EAAU,wCAAA,EAAyC,WAAW,KAAA,EAAO;AAAA;AAAA;AAClG,GAAA,EACJ,CAAA;AAER;ACjCO,SAAS,QAAA,CAAS,EAAE,IAAA,GAAO,GAAA,EAAK,MAAA,GAAS,OAAO,MAAA,GAAS,IAAA,EAAM,KAAA,EAAO,SAAA,EAAU,EAAkB;AACrG,EAAA,uBACID,eAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,GAAG,uBAAA,EAAyB,MAAA,IAAU,oBAAA,EAAsB,SAAS,CAAA,EAAG,KAAA,EAAO,EAAE,KAAA,EAAO,MAAK,EACzG,QAAA,EAAA;AAAA,oBAAAC,cAAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAY,KAAA,EAAc,WAAU,qBAAA,EAAsB,CAAA;AAAA,IAC/D,yBACGA,cAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACG,aAAA,EAAW,IAAA;AAAA,QACX,SAAA,EAAU,gEAAA;AAAA,QACV,KAAA,EAAO,EAAE,MAAA,EAAQ,IAAA,GAAO,IAAA,EAAM,QAAQ,CAAC,IAAA,GAAO,IAAA,EAAM,UAAA,EAAY,uBAAA;AAAwB;AAAA,KAC5F,GACA;AAAA,GAAA,EACR,CAAA;AAER","file":"brand.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { extendTailwindMerge } from 'tailwind-merge';\n\n// tailwind-merge would treat `oca-display-1` and `text-foreground` as the same\n// group and drop one; teach it the package's own type utilities.\nconst merge = extendTailwindMerge<'oca-type'>({\n extend: {\n classGroups: {\n 'oca-type': [\n 'oca-display-1',\n 'oca-display-2',\n 'oca-display-3',\n 'oca-title',\n 'oca-title-sm',\n 'oca-eyebrow',\n 'oca-lead',\n 'oca-body',\n 'oca-numeral',\n ],\n },\n },\n});\n\nexport function cn(...inputs: ClassValue[]): string {\n return merge(clsx(inputs));\n}\n","import type { SVGProps } from 'react';\n\nimport { cn } from '../lib/cn';\n\n/*\n * The isometric check, measured from Sou's deck (slide 9) onto a 16×8 grid:\n * a chevron with a 3-unit short arm and a 5-unit long arm, one unit thick,\n * extruded two units. Three faces, three oranges.\n */\n\nconst TOP = '0,3 2,2 6,4 14,0 16,1 6,6';\nconst LEFT = '0,3 6,6 6,8 0,5';\nconst RIGHT = '6,6 16,1 16,3 6,8';\n\nexport const MARK_FACES = { top: '#FED8AB', left: '#FB933C', right: '#EA580B' } as const;\n\nexport interface MarkProps extends Omit<SVGProps<SVGSVGElement>, 'children'> {\n /** Accessible name. Omit to render the mark as decoration. */\n title?: string;\n /** Rendered width in px; height follows the 2:1 grid. */\n size?: number;\n}\n\nexport function Mark({ title, size, className, ...rest }: MarkProps) {\n return (\n <svg\n viewBox=\"0 0 16 8\"\n width={size}\n height={size ? size / 2 : undefined}\n role={title ? 'img' : undefined}\n aria-hidden={title ? undefined : true}\n className={cn('shrink-0', className)}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n <polygon points={LEFT} fill={MARK_FACES.left} />\n <polygon points={RIGHT} fill={MARK_FACES.right} />\n <polygon points={TOP} fill={MARK_FACES.top} />\n </svg>\n );\n}\n\nexport interface AppIconProps extends Omit<SVGProps<SVGSVGElement>, 'children'> {\n title?: string;\n size?: number;\n}\n\n/** The app icon: the mark on an ink squircle, as in the deck's lockup. */\nexport function AppIcon({ title, size = 32, className, ...rest }: AppIconProps) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n width={size}\n height={size}\n role={title ? 'img' : undefined}\n aria-hidden={title ? undefined : true}\n className={cn('shrink-0', className)}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n <rect\n width=\"24\"\n height=\"24\"\n rx=\"6.25\"\n style={{ fill: 'var(--oca-icon-tile, #0A0A0A)', stroke: 'var(--oca-icon-ring, transparent)' }}\n strokeWidth=\"0.5\"\n />\n <g transform=\"translate(4.25 8.1) scale(0.97)\">\n <polygon points={LEFT} fill={MARK_FACES.left} />\n <polygon points={RIGHT} fill={MARK_FACES.right} />\n <polygon points={TOP} fill={MARK_FACES.top} />\n </g>\n </svg>\n );\n}\n\nexport interface LockupProps {\n /** The word beside the icon. */\n name?: string;\n /** Icon size in px; the word scales with it. */\n size?: number;\n className?: string;\n}\n\n/** Icon and wordmark, lowercase, as the deck sets it. */\nexport function Lockup({ name = 'orangecheck', size = 28, className }: LockupProps) {\n return (\n <span\n className={cn('inline-flex items-center font-medium tracking-[-0.01em]', className)}\n style={{ gap: size * 0.32, fontSize: size * 0.56 }}\n >\n <AppIcon size={size} />\n <span>{name}</span>\n </span>\n );\n}\n","import type { CSSProperties } from 'react';\n\nimport { cn } from '../lib/cn';\n\n/*\n * The deck's photograph: a halftone portrait on an orange sphere that glows\n * near-white behind the subject and saturates at the rim, with a hairline\n * stadium behind it. The dot layer is an image of black dots on transparent,\n * made by scripts/halftone.py from any photo, so the sphere stays CSS and the\n * dots stay crisp at any size.\n */\n\nexport const SPHERE_GRADIENT =\n 'radial-gradient(circle at 50% 42%, #fff8f3 0%, #ffe2d3 22%, #ffb48d 45%, #ff8a55 68%, #ff6a2c 100%)';\n\nexport interface HalftoneSphereProps {\n /** The dot layer (black dots on transparent, already circular). */\n src: string;\n /** Describe the person or subject; empty string marks it decorative. */\n alt: string;\n /** Draw the hairline stadium behind the sphere, as the hero does. */\n stadium?: boolean;\n /** Let the sphere drift a few pixels. Off under reduced motion. */\n float?: boolean;\n className?: string;\n style?: CSSProperties;\n}\n\nexport function HalftoneSphere({ src, alt, stadium = false, float = false, className, style }: HalftoneSphereProps) {\n return (\n <div className={cn('relative aspect-square w-full', className)} style={style}>\n {stadium ? (\n // The deck's offset stadium on wide screens; centred and inside the\n // gutter on phones, where the offset would push the page sideways.\n <div\n aria-hidden\n className=\"oca-stadium pointer-events-none absolute inset-[-6%] md:[inset:-13%_10%_-13%_-27%]\"\n />\n ) : null}\n <div\n className={cn('relative size-full overflow-hidden rounded-full', float && 'oca-animate-float')}\n style={{ background: SPHERE_GRADIENT }}\n >\n {/* eslint-disable-next-line @next/next/no-img-element -- a static dot layer, sized by CSS */}\n <img src={src} alt={alt} className=\"absolute inset-0 size-full select-none\" draggable={false} />\n </div>\n </div>\n );\n}\n","import { cn } from '../lib/cn';\nimport { Mark } from './mark';\n\nexport interface IsoCheckProps {\n /** Width in px; the check keeps its 2:1 grid. */\n size?: number;\n /** Drop it into place on first paint, the deck's closing gesture. */\n settle?: boolean;\n /** A soft floor shadow, so it reads as an object on the canvas. */\n shadow?: boolean;\n title?: string;\n className?: string;\n}\n\n/** The isometric check as an object: the deck's closing illustration. */\nexport function IsoCheck({ size = 360, settle = false, shadow = true, title, className }: IsoCheckProps) {\n return (\n <div className={cn('relative inline-block', settle && 'oca-animate-settle', className)} style={{ width: size }}>\n <Mark size={size} title={title} className=\"relative z-10 block\" />\n {shadow ? (\n <div\n aria-hidden\n className=\"absolute left-[8%] right-[8%] rounded-[50%] opacity-60 blur-xl\"\n style={{ height: size * 0.08, bottom: -size * 0.04, background: 'rgb(234 88 11 / 0.18)' }}\n />\n ) : null}\n </div>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/lib/cn.ts","../src/brand/mark.tsx","../src/brand/sphere.tsx","../src/brand/iso-check.tsx"],"names":["extendTailwindMerge","clsx","jsxs","jsx"],"mappings":"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","file":"brand.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { extendTailwindMerge } from 'tailwind-merge';\n\n// tailwind-merge would treat `oca-display-1` and `text-foreground` as the same\n// group and drop one; teach it the package's own type utilities.\nconst merge = extendTailwindMerge<'oca-type'>({\n extend: {\n classGroups: {\n 'oca-type': [\n 'oca-display-1',\n 'oca-display-2',\n 'oca-display-3',\n 'oca-title',\n 'oca-title-sm',\n 'oca-eyebrow',\n 'oca-lead',\n 'oca-body',\n 'oca-numeral',\n ],\n },\n },\n});\n\nexport function cn(...inputs: ClassValue[]): string {\n return merge(clsx(inputs));\n}\n","import type { SVGProps } from 'react';\n\nimport { cn } from '../lib/cn';\n\n/*\n * The isometric check, measured from Sou's deck (slide 9) onto a 16×8 grid:\n * a chevron with a 3-unit short arm and a 5-unit long arm, one unit thick,\n * extruded two units. Three faces, three oranges.\n */\n\nconst TOP = '0,3 2,2 6,4 14,0 16,1 6,6';\nconst LEFT = '0,3 6,6 6,8 0,5';\nconst RIGHT = '6,6 16,1 16,3 6,8';\n\nexport const MARK_FACES = { top: '#FED8AB', left: '#FB933C', right: '#EA580B' } as const;\n\nexport interface MarkProps extends Omit<SVGProps<SVGSVGElement>, 'children'> {\n /** Accessible name. Omit to render the mark as decoration. */\n title?: string;\n /** Rendered width in px; height follows the 2:1 grid. */\n size?: number;\n}\n\nexport function Mark({ title, size, className, ...rest }: MarkProps) {\n return (\n <svg\n viewBox=\"0 0 16 8\"\n width={size}\n height={size ? size / 2 : undefined}\n role={title ? 'img' : undefined}\n aria-hidden={title ? undefined : true}\n className={cn('shrink-0', className)}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n <polygon points={LEFT} fill={MARK_FACES.left} />\n <polygon points={RIGHT} fill={MARK_FACES.right} />\n <polygon points={TOP} fill={MARK_FACES.top} />\n </svg>\n );\n}\n\nexport interface AppIconProps extends Omit<SVGProps<SVGSVGElement>, 'children'> {\n title?: string;\n size?: number;\n}\n\n/** The app icon: the mark on an ink squircle, as in the deck's lockup. */\nexport function AppIcon({ title, size = 32, className, ...rest }: AppIconProps) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n width={size}\n height={size}\n role={title ? 'img' : undefined}\n aria-hidden={title ? undefined : true}\n className={cn('shrink-0', className)}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n <rect\n width=\"24\"\n height=\"24\"\n rx=\"6.25\"\n style={{ fill: 'var(--oca-icon-tile, #0A0A0A)', stroke: 'var(--oca-icon-ring, transparent)' }}\n strokeWidth=\"0.5\"\n />\n <g transform=\"translate(4.25 8.1) scale(0.97)\">\n <polygon points={LEFT} fill={MARK_FACES.left} />\n <polygon points={RIGHT} fill={MARK_FACES.right} />\n <polygon points={TOP} fill={MARK_FACES.top} />\n </g>\n </svg>\n );\n}\n\nexport interface LockupProps {\n /** The word beside the icon. */\n name?: string;\n /** Icon size in px; the word scales with it. */\n size?: number;\n className?: string;\n}\n\n/** Icon and wordmark, lowercase, as the deck sets it. */\nexport function Lockup({ name = 'orangecheck', size = 28, className }: LockupProps) {\n return (\n <span\n className={cn('inline-flex items-center font-medium tracking-[-0.01em]', className)}\n style={{ gap: size * 0.32, fontSize: size * 0.56 }}\n >\n <AppIcon size={size} />\n <span>{name}</span>\n </span>\n );\n}\n","import type { CSSProperties } from 'react';\n\nimport { cn } from '../lib/cn';\n\n/*\n * The deck's photograph: a halftone portrait on an orange sphere that glows\n * near-white behind the subject and saturates at the rim, with a hairline\n * stadium behind it. The dot layer is an image of black dots on transparent,\n * made by scripts/halftone.py from any photo, so the sphere stays CSS and the\n * dots stay crisp at any size.\n */\n\nexport const SPHERE_GRADIENT =\n 'radial-gradient(circle at 50% 42%, #fff8f3 0%, #ffe2d3 22%, #ffb48d 45%, #ff8a55 68%, #ff6a2c 100%)';\n\nexport interface HalftoneSphereProps {\n /** The dot layer (black dots on transparent, already circular). */\n src: string;\n /** Describe the person or subject; empty string marks it decorative. */\n alt: string;\n /** Draw the hairline stadium behind the sphere, as the hero does. */\n stadium?: boolean;\n /** Let the sphere drift a few pixels. Off under reduced motion. */\n float?: boolean;\n className?: string;\n style?: CSSProperties;\n}\n\nexport function HalftoneSphere({ src, alt, stadium = false, float = false, className, style }: HalftoneSphereProps) {\n return (\n <div className={cn('relative aspect-square w-full', className)} style={style}>\n {stadium ? (\n // Measured from the deck's title slide: a rounded square, its corners\n // 17% of the sphere, level with the sphere's foot, reaching left behind\n // the headline. On phones a ring inside the gutter, since the offset\n // would push the page sideways.\n <div\n aria-hidden\n className=\"oca-stadium pointer-events-none absolute inset-[-6%] rounded-full md:[inset:-12%_14%_0%_-26%] md:rounded-[17%]\"\n />\n ) : null}\n <div\n className={cn('relative size-full overflow-hidden rounded-full', float && 'oca-animate-float')}\n style={{ background: SPHERE_GRADIENT }}\n >\n {/* eslint-disable-next-line @next/next/no-img-element -- a static dot layer, sized by CSS */}\n <img src={src} alt={alt} className=\"absolute inset-0 size-full select-none\" draggable={false} />\n </div>\n </div>\n );\n}\n","import { cn } from '../lib/cn';\nimport { Mark } from './mark';\n\nexport interface IsoCheckProps {\n /** Width in px; the check keeps its 2:1 grid. */\n size?: number;\n /** Drop it into place on first paint, the deck's closing gesture. */\n settle?: boolean;\n /** A soft floor shadow, so it reads as an object on the canvas. */\n shadow?: boolean;\n title?: string;\n className?: string;\n}\n\n/** The isometric check as an object: the deck's closing illustration. */\nexport function IsoCheck({ size = 360, settle = false, shadow = true, title, className }: IsoCheckProps) {\n return (\n <div className={cn('relative inline-block', settle && 'oca-animate-settle', className)} style={{ width: size }}>\n <Mark size={size} title={title} className=\"relative z-10 block\" />\n {shadow ? (\n <div\n aria-hidden\n className=\"absolute left-[8%] right-[8%] rounded-[50%] opacity-60 blur-xl\"\n style={{ height: size * 0.08, bottom: -size * 0.04, background: 'rgb(234 88 11 / 0.18)' }}\n />\n ) : null}\n </div>\n );\n}\n"]}
package/dist/brand.mjs CHANGED
@@ -95,13 +95,15 @@ var SPHERE_GRADIENT = "radial-gradient(circle at 50% 42%, #fff8f3 0%, #ffe2d3 22
95
95
  function HalftoneSphere({ src, alt, stadium = false, float = false, className, style }) {
96
96
  return /* @__PURE__ */ jsxs("div", { className: cn("relative aspect-square w-full", className), style, children: [
97
97
  stadium ? (
98
- // The deck's offset stadium on wide screens; centred and inside the
99
- // gutter on phones, where the offset would push the page sideways.
98
+ // Measured from the deck's title slide: a rounded square, its corners
99
+ // 17% of the sphere, level with the sphere's foot, reaching left behind
100
+ // the headline. On phones a ring inside the gutter, since the offset
101
+ // would push the page sideways.
100
102
  /* @__PURE__ */ jsx(
101
103
  "div",
102
104
  {
103
105
  "aria-hidden": true,
104
- className: "oca-stadium pointer-events-none absolute inset-[-6%] md:[inset:-13%_10%_-13%_-27%]"
106
+ className: "oca-stadium pointer-events-none absolute inset-[-6%] rounded-full md:[inset:-12%_14%_0%_-26%] md:rounded-[17%]"
105
107
  }
106
108
  )
107
109
  ) : null,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/cn.ts","../src/brand/mark.tsx","../src/brand/sphere.tsx","../src/brand/iso-check.tsx"],"names":["jsxs","jsx"],"mappings":"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","file":"brand.mjs","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { extendTailwindMerge } from 'tailwind-merge';\n\n// tailwind-merge would treat `oca-display-1` and `text-foreground` as the same\n// group and drop one; teach it the package's own type utilities.\nconst merge = extendTailwindMerge<'oca-type'>({\n extend: {\n classGroups: {\n 'oca-type': [\n 'oca-display-1',\n 'oca-display-2',\n 'oca-display-3',\n 'oca-title',\n 'oca-title-sm',\n 'oca-eyebrow',\n 'oca-lead',\n 'oca-body',\n 'oca-numeral',\n ],\n },\n },\n});\n\nexport function cn(...inputs: ClassValue[]): string {\n return merge(clsx(inputs));\n}\n","import type { SVGProps } from 'react';\n\nimport { cn } from '../lib/cn';\n\n/*\n * The isometric check, measured from Sou's deck (slide 9) onto a 16×8 grid:\n * a chevron with a 3-unit short arm and a 5-unit long arm, one unit thick,\n * extruded two units. Three faces, three oranges.\n */\n\nconst TOP = '0,3 2,2 6,4 14,0 16,1 6,6';\nconst LEFT = '0,3 6,6 6,8 0,5';\nconst RIGHT = '6,6 16,1 16,3 6,8';\n\nexport const MARK_FACES = { top: '#FED8AB', left: '#FB933C', right: '#EA580B' } as const;\n\nexport interface MarkProps extends Omit<SVGProps<SVGSVGElement>, 'children'> {\n /** Accessible name. Omit to render the mark as decoration. */\n title?: string;\n /** Rendered width in px; height follows the 2:1 grid. */\n size?: number;\n}\n\nexport function Mark({ title, size, className, ...rest }: MarkProps) {\n return (\n <svg\n viewBox=\"0 0 16 8\"\n width={size}\n height={size ? size / 2 : undefined}\n role={title ? 'img' : undefined}\n aria-hidden={title ? undefined : true}\n className={cn('shrink-0', className)}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n <polygon points={LEFT} fill={MARK_FACES.left} />\n <polygon points={RIGHT} fill={MARK_FACES.right} />\n <polygon points={TOP} fill={MARK_FACES.top} />\n </svg>\n );\n}\n\nexport interface AppIconProps extends Omit<SVGProps<SVGSVGElement>, 'children'> {\n title?: string;\n size?: number;\n}\n\n/** The app icon: the mark on an ink squircle, as in the deck's lockup. */\nexport function AppIcon({ title, size = 32, className, ...rest }: AppIconProps) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n width={size}\n height={size}\n role={title ? 'img' : undefined}\n aria-hidden={title ? undefined : true}\n className={cn('shrink-0', className)}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n <rect\n width=\"24\"\n height=\"24\"\n rx=\"6.25\"\n style={{ fill: 'var(--oca-icon-tile, #0A0A0A)', stroke: 'var(--oca-icon-ring, transparent)' }}\n strokeWidth=\"0.5\"\n />\n <g transform=\"translate(4.25 8.1) scale(0.97)\">\n <polygon points={LEFT} fill={MARK_FACES.left} />\n <polygon points={RIGHT} fill={MARK_FACES.right} />\n <polygon points={TOP} fill={MARK_FACES.top} />\n </g>\n </svg>\n );\n}\n\nexport interface LockupProps {\n /** The word beside the icon. */\n name?: string;\n /** Icon size in px; the word scales with it. */\n size?: number;\n className?: string;\n}\n\n/** Icon and wordmark, lowercase, as the deck sets it. */\nexport function Lockup({ name = 'orangecheck', size = 28, className }: LockupProps) {\n return (\n <span\n className={cn('inline-flex items-center font-medium tracking-[-0.01em]', className)}\n style={{ gap: size * 0.32, fontSize: size * 0.56 }}\n >\n <AppIcon size={size} />\n <span>{name}</span>\n </span>\n );\n}\n","import type { CSSProperties } from 'react';\n\nimport { cn } from '../lib/cn';\n\n/*\n * The deck's photograph: a halftone portrait on an orange sphere that glows\n * near-white behind the subject and saturates at the rim, with a hairline\n * stadium behind it. The dot layer is an image of black dots on transparent,\n * made by scripts/halftone.py from any photo, so the sphere stays CSS and the\n * dots stay crisp at any size.\n */\n\nexport const SPHERE_GRADIENT =\n 'radial-gradient(circle at 50% 42%, #fff8f3 0%, #ffe2d3 22%, #ffb48d 45%, #ff8a55 68%, #ff6a2c 100%)';\n\nexport interface HalftoneSphereProps {\n /** The dot layer (black dots on transparent, already circular). */\n src: string;\n /** Describe the person or subject; empty string marks it decorative. */\n alt: string;\n /** Draw the hairline stadium behind the sphere, as the hero does. */\n stadium?: boolean;\n /** Let the sphere drift a few pixels. Off under reduced motion. */\n float?: boolean;\n className?: string;\n style?: CSSProperties;\n}\n\nexport function HalftoneSphere({ src, alt, stadium = false, float = false, className, style }: HalftoneSphereProps) {\n return (\n <div className={cn('relative aspect-square w-full', className)} style={style}>\n {stadium ? (\n // The deck's offset stadium on wide screens; centred and inside the\n // gutter on phones, where the offset would push the page sideways.\n <div\n aria-hidden\n className=\"oca-stadium pointer-events-none absolute inset-[-6%] md:[inset:-13%_10%_-13%_-27%]\"\n />\n ) : null}\n <div\n className={cn('relative size-full overflow-hidden rounded-full', float && 'oca-animate-float')}\n style={{ background: SPHERE_GRADIENT }}\n >\n {/* eslint-disable-next-line @next/next/no-img-element -- a static dot layer, sized by CSS */}\n <img src={src} alt={alt} className=\"absolute inset-0 size-full select-none\" draggable={false} />\n </div>\n </div>\n );\n}\n","import { cn } from '../lib/cn';\nimport { Mark } from './mark';\n\nexport interface IsoCheckProps {\n /** Width in px; the check keeps its 2:1 grid. */\n size?: number;\n /** Drop it into place on first paint, the deck's closing gesture. */\n settle?: boolean;\n /** A soft floor shadow, so it reads as an object on the canvas. */\n shadow?: boolean;\n title?: string;\n className?: string;\n}\n\n/** The isometric check as an object: the deck's closing illustration. */\nexport function IsoCheck({ size = 360, settle = false, shadow = true, title, className }: IsoCheckProps) {\n return (\n <div className={cn('relative inline-block', settle && 'oca-animate-settle', className)} style={{ width: size }}>\n <Mark size={size} title={title} className=\"relative z-10 block\" />\n {shadow ? (\n <div\n aria-hidden\n className=\"absolute left-[8%] right-[8%] rounded-[50%] opacity-60 blur-xl\"\n style={{ height: size * 0.08, bottom: -size * 0.04, background: 'rgb(234 88 11 / 0.18)' }}\n />\n ) : null}\n </div>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/lib/cn.ts","../src/brand/mark.tsx","../src/brand/sphere.tsx","../src/brand/iso-check.tsx"],"names":["jsxs","jsx"],"mappings":"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","file":"brand.mjs","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { extendTailwindMerge } from 'tailwind-merge';\n\n// tailwind-merge would treat `oca-display-1` and `text-foreground` as the same\n// group and drop one; teach it the package's own type utilities.\nconst merge = extendTailwindMerge<'oca-type'>({\n extend: {\n classGroups: {\n 'oca-type': [\n 'oca-display-1',\n 'oca-display-2',\n 'oca-display-3',\n 'oca-title',\n 'oca-title-sm',\n 'oca-eyebrow',\n 'oca-lead',\n 'oca-body',\n 'oca-numeral',\n ],\n },\n },\n});\n\nexport function cn(...inputs: ClassValue[]): string {\n return merge(clsx(inputs));\n}\n","import type { SVGProps } from 'react';\n\nimport { cn } from '../lib/cn';\n\n/*\n * The isometric check, measured from Sou's deck (slide 9) onto a 16×8 grid:\n * a chevron with a 3-unit short arm and a 5-unit long arm, one unit thick,\n * extruded two units. Three faces, three oranges.\n */\n\nconst TOP = '0,3 2,2 6,4 14,0 16,1 6,6';\nconst LEFT = '0,3 6,6 6,8 0,5';\nconst RIGHT = '6,6 16,1 16,3 6,8';\n\nexport const MARK_FACES = { top: '#FED8AB', left: '#FB933C', right: '#EA580B' } as const;\n\nexport interface MarkProps extends Omit<SVGProps<SVGSVGElement>, 'children'> {\n /** Accessible name. Omit to render the mark as decoration. */\n title?: string;\n /** Rendered width in px; height follows the 2:1 grid. */\n size?: number;\n}\n\nexport function Mark({ title, size, className, ...rest }: MarkProps) {\n return (\n <svg\n viewBox=\"0 0 16 8\"\n width={size}\n height={size ? size / 2 : undefined}\n role={title ? 'img' : undefined}\n aria-hidden={title ? undefined : true}\n className={cn('shrink-0', className)}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n <polygon points={LEFT} fill={MARK_FACES.left} />\n <polygon points={RIGHT} fill={MARK_FACES.right} />\n <polygon points={TOP} fill={MARK_FACES.top} />\n </svg>\n );\n}\n\nexport interface AppIconProps extends Omit<SVGProps<SVGSVGElement>, 'children'> {\n title?: string;\n size?: number;\n}\n\n/** The app icon: the mark on an ink squircle, as in the deck's lockup. */\nexport function AppIcon({ title, size = 32, className, ...rest }: AppIconProps) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n width={size}\n height={size}\n role={title ? 'img' : undefined}\n aria-hidden={title ? undefined : true}\n className={cn('shrink-0', className)}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n <rect\n width=\"24\"\n height=\"24\"\n rx=\"6.25\"\n style={{ fill: 'var(--oca-icon-tile, #0A0A0A)', stroke: 'var(--oca-icon-ring, transparent)' }}\n strokeWidth=\"0.5\"\n />\n <g transform=\"translate(4.25 8.1) scale(0.97)\">\n <polygon points={LEFT} fill={MARK_FACES.left} />\n <polygon points={RIGHT} fill={MARK_FACES.right} />\n <polygon points={TOP} fill={MARK_FACES.top} />\n </g>\n </svg>\n );\n}\n\nexport interface LockupProps {\n /** The word beside the icon. */\n name?: string;\n /** Icon size in px; the word scales with it. */\n size?: number;\n className?: string;\n}\n\n/** Icon and wordmark, lowercase, as the deck sets it. */\nexport function Lockup({ name = 'orangecheck', size = 28, className }: LockupProps) {\n return (\n <span\n className={cn('inline-flex items-center font-medium tracking-[-0.01em]', className)}\n style={{ gap: size * 0.32, fontSize: size * 0.56 }}\n >\n <AppIcon size={size} />\n <span>{name}</span>\n </span>\n );\n}\n","import type { CSSProperties } from 'react';\n\nimport { cn } from '../lib/cn';\n\n/*\n * The deck's photograph: a halftone portrait on an orange sphere that glows\n * near-white behind the subject and saturates at the rim, with a hairline\n * stadium behind it. The dot layer is an image of black dots on transparent,\n * made by scripts/halftone.py from any photo, so the sphere stays CSS and the\n * dots stay crisp at any size.\n */\n\nexport const SPHERE_GRADIENT =\n 'radial-gradient(circle at 50% 42%, #fff8f3 0%, #ffe2d3 22%, #ffb48d 45%, #ff8a55 68%, #ff6a2c 100%)';\n\nexport interface HalftoneSphereProps {\n /** The dot layer (black dots on transparent, already circular). */\n src: string;\n /** Describe the person or subject; empty string marks it decorative. */\n alt: string;\n /** Draw the hairline stadium behind the sphere, as the hero does. */\n stadium?: boolean;\n /** Let the sphere drift a few pixels. Off under reduced motion. */\n float?: boolean;\n className?: string;\n style?: CSSProperties;\n}\n\nexport function HalftoneSphere({ src, alt, stadium = false, float = false, className, style }: HalftoneSphereProps) {\n return (\n <div className={cn('relative aspect-square w-full', className)} style={style}>\n {stadium ? (\n // Measured from the deck's title slide: a rounded square, its corners\n // 17% of the sphere, level with the sphere's foot, reaching left behind\n // the headline. On phones a ring inside the gutter, since the offset\n // would push the page sideways.\n <div\n aria-hidden\n className=\"oca-stadium pointer-events-none absolute inset-[-6%] rounded-full md:[inset:-12%_14%_0%_-26%] md:rounded-[17%]\"\n />\n ) : null}\n <div\n className={cn('relative size-full overflow-hidden rounded-full', float && 'oca-animate-float')}\n style={{ background: SPHERE_GRADIENT }}\n >\n {/* eslint-disable-next-line @next/next/no-img-element -- a static dot layer, sized by CSS */}\n <img src={src} alt={alt} className=\"absolute inset-0 size-full select-none\" draggable={false} />\n </div>\n </div>\n );\n}\n","import { cn } from '../lib/cn';\nimport { Mark } from './mark';\n\nexport interface IsoCheckProps {\n /** Width in px; the check keeps its 2:1 grid. */\n size?: number;\n /** Drop it into place on first paint, the deck's closing gesture. */\n settle?: boolean;\n /** A soft floor shadow, so it reads as an object on the canvas. */\n shadow?: boolean;\n title?: string;\n className?: string;\n}\n\n/** The isometric check as an object: the deck's closing illustration. */\nexport function IsoCheck({ size = 360, settle = false, shadow = true, title, className }: IsoCheckProps) {\n return (\n <div className={cn('relative inline-block', settle && 'oca-animate-settle', className)} style={{ width: size }}>\n <Mark size={size} title={title} className=\"relative z-10 block\" />\n {shadow ? (\n <div\n aria-hidden\n className=\"absolute left-[8%] right-[8%] rounded-[50%] opacity-60 blur-xl\"\n style={{ height: size * 0.08, bottom: -size * 0.04, background: 'rgb(234 88 11 / 0.18)' }}\n />\n ) : null}\n </div>\n );\n}\n"]}
package/dist/index.js CHANGED
@@ -750,13 +750,15 @@ var SPHERE_GRADIENT = "radial-gradient(circle at 50% 42%, #fff8f3 0%, #ffe2d3 22
750
750
  function HalftoneSphere({ src, alt, stadium = false, float = false, className, style }) {
751
751
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative aspect-square w-full", className), style, children: [
752
752
  stadium ? (
753
- // The deck's offset stadium on wide screens; centred and inside the
754
- // gutter on phones, where the offset would push the page sideways.
753
+ // Measured from the deck's title slide: a rounded square, its corners
754
+ // 17% of the sphere, level with the sphere's foot, reaching left behind
755
+ // the headline. On phones a ring inside the gutter, since the offset
756
+ // would push the page sideways.
755
757
  /* @__PURE__ */ jsxRuntime.jsx(
756
758
  "div",
757
759
  {
758
760
  "aria-hidden": true,
759
- className: "oca-stadium pointer-events-none absolute inset-[-6%] md:[inset:-13%_10%_-13%_-27%]"
761
+ className: "oca-stadium pointer-events-none absolute inset-[-6%] rounded-full md:[inset:-12%_14%_0%_-26%] md:rounded-[17%]"
760
762
  }
761
763
  )
762
764
  ) : null,