@code-sherpas/pharos-react 0.3.0 → 0.5.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 CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  React component library for Code Sherpas. Consumes
4
4
  [`@code-sherpas/pharos-tokens`](https://github.com/code-sherpas/pharos-tokens)
5
- as a peer dependency. Built with Vite library mode, React 19, Tailwind v4.
5
+ as a peer dependency. Built with Vite library mode, React 19 and CSS Modules.
6
+ The published bundle ships zero Tailwind, zero PostCSS preflight and zero
7
+ global resets — see Decision D9 in `PLAN-pharos-alexandria.md`.
6
8
 
7
9
  ## What's in this package
8
10
 
@@ -22,15 +24,16 @@ explicitly alongside this package.
22
24
  ## Usage
23
25
 
24
26
  ```ts
25
- // Root entry file (e.g. main.tsx, app/layout.tsx)
26
- import '@code-sherpas/pharos-react/styles.css';
27
+ // Root entry file (e.g. main.tsx, app/layout.tsx). Import once.
28
+ import '@code-sherpas/pharos-tokens/css'; // CSS variables for the design tokens
29
+ import '@code-sherpas/pharos-react/styles.css'; // hashed component styles
27
30
  ```
28
31
 
29
32
  ```tsx
30
- import { PharosHello } from '@code-sherpas/pharos-react';
33
+ import { Button } from '@code-sherpas/pharos-react';
31
34
 
32
35
  export function Demo() {
33
- return <PharosHello name="Alexandria" />;
36
+ return <Button intent="primary">Save changes</Button>;
34
37
  }
35
38
  ```
36
39
 
@@ -50,7 +53,8 @@ The live component reference runs on Storybook 10 + `@storybook/react-vite`.
50
53
  ## Contributing
51
54
 
52
55
  1. `pnpm install`
53
- 2. Implement the component under `src/components/<Name>.tsx`, export it from
56
+ 2. Implement the component under `src/components/<Name>.tsx` plus its
57
+ sibling `src/components/<Name>.module.css` (Decision D9). Export it from
54
58
  `src/index.ts`.
55
59
  3. Add a story alongside: `src/components/<Name>.stories.tsx`.
56
60
  4. Add tests under `tests/` (Vitest + Testing Library).
@@ -0,0 +1,23 @@
1
+ import { ComponentPropsWithoutRef } from 'react';
2
+ import { VariantProps } from 'class-variance-authority';
3
+ /**
4
+ * Maps the `variant` axis to per-class names inside `Badge.module.css`.
5
+ * The variant set follows shadcn/ui — `default`, `secondary`, `destructive`,
6
+ * `outline` — extended with the semantic tones every modern DS exposes
7
+ * (`success`, `warning`, `info`). Naming stays asymmetric on purpose:
8
+ * `destructive` is the canonical shadcn name for "negative tone" and we
9
+ * keep it instead of renaming it to `error`, so consumers familiar with
10
+ * shadcn pick the right value without re-learning the API.
11
+ */
12
+ declare const badgeVariants: (props?: ({
13
+ variant?: "secondary" | "destructive" | "default" | "outline" | "success" | "warning" | "info" | null | undefined;
14
+ } & import('class-variance-authority/types').ClassProp) | undefined) => string;
15
+ type BadgeVariantProps = VariantProps<typeof badgeVariants>;
16
+ export interface BadgeProps extends ComponentPropsWithoutRef<'span'>, BadgeVariantProps {
17
+ }
18
+ export declare function Badge({ variant, className, ...rest }: BadgeProps): import("react/jsx-runtime").JSX.Element;
19
+ export declare namespace Badge {
20
+ var displayName: string;
21
+ }
22
+ export { badgeVariants };
23
+ //# sourceMappingURL=Badge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Badge.d.ts","sourceRoot":"","sources":["../../src/components/Badge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,OAAO,CAAC;AACtD,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAIlE;;;;;;;;GAQG;AACH,QAAA,MAAM,aAAa;;8EAejB,CAAC;AAEH,KAAK,iBAAiB,GAAG,YAAY,CAAC,OAAO,aAAa,CAAC,CAAC;AAE5D,MAAM,WAAW,UAAW,SAAQ,wBAAwB,CAAC,MAAM,CAAC,EAAE,iBAAiB;CAAG;AAE1F,wBAAgB,KAAK,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,UAAU,2CAShE;yBATe,KAAK;;;AAarB,OAAO,EAAE,aAAa,EAAE,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { Button, buttonVariants } from './components/Button';
2
2
  export type { ButtonProps } from './components/Button';
3
- export { PharosHello } from './components/PharosHello';
4
- export type { PharosHelloProps } from './components/PharosHello';
3
+ export { Badge, badgeVariants } from './components/Badge';
4
+ export type { BadgeProps } from './components/Badge';
5
5
  export { cn } from './lib/cn';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC7D,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,YAAY,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC7D,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAC1D,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -1,41 +1,41 @@
1
1
  import { useRender as e } from "@base-ui/react/use-render";
2
- import { jsx as t, jsxs as n } from "react/jsx-runtime";
2
+ import { jsx as t } from "react/jsx-runtime";
3
3
  //#region node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
4
- function r(e) {
5
- var t, n, i = "";
4
+ function n(e) {
5
+ var t, r, i = "";
6
6
  if (typeof e == "string" || typeof e == "number") i += e;
7
7
  else if (typeof e == "object") if (Array.isArray(e)) {
8
8
  var a = e.length;
9
- for (t = 0; t < a; t++) e[t] && (n = r(e[t])) && (i && (i += " "), i += n);
10
- } else for (n in e) e[n] && (i && (i += " "), i += n);
9
+ for (t = 0; t < a; t++) e[t] && (r = n(e[t])) && (i && (i += " "), i += r);
10
+ } else for (r in e) e[r] && (i && (i += " "), i += r);
11
11
  return i;
12
12
  }
13
- function i() {
14
- for (var e, t, n = 0, i = "", a = arguments.length; n < a; n++) (e = arguments[n]) && (t = r(e)) && (i && (i += " "), i += t);
13
+ function r() {
14
+ for (var e, t, r = 0, i = "", a = arguments.length; r < a; r++) (e = arguments[r]) && (t = n(e)) && (i && (i += " "), i += t);
15
15
  return i;
16
16
  }
17
17
  //#endregion
18
18
  //#region node_modules/.pnpm/class-variance-authority@0.7.1/node_modules/class-variance-authority/dist/index.mjs
19
- var a = (e) => typeof e == "boolean" ? `${e}` : e === 0 ? "0" : e, o = i, s = (e, t) => (n) => {
20
- if (t?.variants == null) return o(e, n?.class, n?.className);
21
- let { variants: r, defaultVariants: i } = t, s = Object.keys(r).map((e) => {
22
- let t = n?.[e], o = i?.[e];
19
+ var i = (e) => typeof e == "boolean" ? `${e}` : e === 0 ? "0" : e, a = r, o = (e, t) => (n) => {
20
+ if (t?.variants == null) return a(e, n?.class, n?.className);
21
+ let { variants: r, defaultVariants: o } = t, s = Object.keys(r).map((e) => {
22
+ let t = n?.[e], a = o?.[e];
23
23
  if (t === null) return null;
24
- let s = a(t) || a(o);
24
+ let s = i(t) || i(a);
25
25
  return r[e][s];
26
26
  }), c = n && Object.entries(n).reduce((e, t) => {
27
27
  let [n, r] = t;
28
28
  return r === void 0 || (e[n] = r), e;
29
29
  }, {});
30
- return o(e, s, t?.compoundVariants?.reduce((e, t) => {
31
- let { class: n, className: r, ...a } = t;
32
- return Object.entries(a).every((e) => {
30
+ return a(e, s, t?.compoundVariants?.reduce((e, t) => {
31
+ let { class: n, className: r, ...i } = t;
32
+ return Object.entries(i).every((e) => {
33
33
  let [t, n] = e;
34
34
  return Array.isArray(n) ? n.includes({
35
- ...i,
35
+ ...o,
36
36
  ...c
37
37
  }[t]) : {
38
- ...i,
38
+ ...o,
39
39
  ...c
40
40
  }[t] === n;
41
41
  }) ? [
@@ -47,10 +47,10 @@ var a = (e) => typeof e == "boolean" ? `${e}` : e === 0 ? "0" : e, o = i, s = (e
47
47
  };
48
48
  //#endregion
49
49
  //#region src/lib/cn.ts
50
- function c(...e) {
51
- return i(e);
50
+ function s(...e) {
51
+ return r(e);
52
52
  }
53
- var l = {
53
+ var c = {
54
54
  button: "_button_6sqxz_18",
55
55
  primary: "_primary_6sqxz_127",
56
56
  secondary: "_secondary_6sqxz_141",
@@ -59,18 +59,18 @@ var l = {
59
59
  sizeSm: "_sizeSm_6sqxz_193",
60
60
  sizeMd: "_sizeMd_6sqxz_201",
61
61
  sizeLg: "_sizeLg_6sqxz_209"
62
- }, u = s(l.button, {
62
+ }, l = o(c.button, {
63
63
  variants: {
64
64
  intent: {
65
- primary: l.primary,
66
- secondary: l.secondary,
67
- ghost: l.ghost,
68
- destructive: l.destructive
65
+ primary: c.primary,
66
+ secondary: c.secondary,
67
+ ghost: c.ghost,
68
+ destructive: c.destructive
69
69
  },
70
70
  size: {
71
- sm: l.sizeSm,
72
- md: l.sizeMd,
73
- lg: l.sizeLg
71
+ sm: c.sizeSm,
72
+ md: c.sizeMd,
73
+ lg: c.sizeLg
74
74
  }
75
75
  },
76
76
  defaultVariants: {
@@ -78,40 +78,56 @@ var l = {
78
78
  size: "md"
79
79
  }
80
80
  });
81
- function d({ render: n, className: r, intent: i, size: a, type: o = "button", ref: s, ...l }) {
81
+ function u({ render: n, className: r, intent: i, size: a, type: o = "button", ref: c, ...u }) {
82
82
  let d = i ?? "primary", f = a ?? "md";
83
83
  return e({
84
84
  render: n ?? /* @__PURE__ */ t("button", {}),
85
- ref: s,
85
+ ref: c,
86
86
  defaultTagName: "button",
87
87
  props: {
88
88
  type: o,
89
89
  "data-pharos-intent": d,
90
90
  "data-pharos-size": f,
91
- className: c(u({
91
+ className: s(l({
92
92
  intent: d,
93
93
  size: f
94
94
  }), r),
95
- ...l
95
+ ...u
96
96
  }
97
97
  });
98
98
  }
99
- d.displayName = "Button";
100
- var f = { hello: "_hello_13wdd_8" };
101
- //#endregion
102
- //#region src/components/PharosHello.tsx
103
- function p({ name: e = "world", className: t, ...r }) {
104
- return /* @__PURE__ */ n("div", {
105
- className: c(f.hello, t),
106
- ...r,
107
- children: [
108
- "Pharos says hello, ",
109
- e,
110
- "."
111
- ]
99
+ u.displayName = "Button";
100
+ var d = {
101
+ badge: "_badge_rqyn4_5",
102
+ default: "_default_rqyn4_30",
103
+ secondary: "_secondary_rqyn4_35",
104
+ destructive: "_destructive_rqyn4_40",
105
+ outline: "_outline_rqyn4_45",
106
+ success: "_success_rqyn4_51",
107
+ warning: "_warning_rqyn4_56",
108
+ info: "_info_rqyn4_61"
109
+ }, f = o(d.badge, {
110
+ variants: { variant: {
111
+ default: d.default,
112
+ secondary: d.secondary,
113
+ destructive: d.destructive,
114
+ outline: d.outline,
115
+ success: d.success,
116
+ warning: d.warning,
117
+ info: d.info
118
+ } },
119
+ defaultVariants: { variant: "default" }
120
+ });
121
+ function p({ variant: e, className: n, ...r }) {
122
+ let i = e ?? "default";
123
+ return /* @__PURE__ */ t("span", {
124
+ "data-pharos-variant": i,
125
+ className: s(f({ variant: i }), n),
126
+ ...r
112
127
  });
113
128
  }
129
+ p.displayName = "Badge";
114
130
  //#endregion
115
- export { d as Button, p as PharosHello, u as buttonVariants, c as cn };
131
+ export { p as Badge, u as Button, f as badgeVariants, l as buttonVariants, s as cn };
116
132
 
117
133
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs","../node_modules/.pnpm/class-variance-authority@0.7.1/node_modules/class-variance-authority/dist/index.mjs","../src/lib/cn.ts","../src/components/Button.module.css","../src/components/Button.tsx","../src/components/PharosHello.module.css","../src/components/PharosHello.tsx"],"sourcesContent":["function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","/**\n * Copyright 2022 Joe Bell. All rights reserved.\n *\n * This file is licensed to you under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with the\n * License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */ import { clsx } from \"clsx\";\nconst falsyToString = (value)=>typeof value === \"boolean\" ? `${value}` : value === 0 ? \"0\" : value;\nexport const cx = clsx;\nexport const cva = (base, config)=>(props)=>{\n var _config_compoundVariants;\n if ((config === null || config === void 0 ? void 0 : config.variants) == null) return cx(base, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);\n const { variants, defaultVariants } = config;\n const getVariantClassNames = Object.keys(variants).map((variant)=>{\n const variantProp = props === null || props === void 0 ? void 0 : props[variant];\n const defaultVariantProp = defaultVariants === null || defaultVariants === void 0 ? void 0 : defaultVariants[variant];\n if (variantProp === null) return null;\n const variantKey = falsyToString(variantProp) || falsyToString(defaultVariantProp);\n return variants[variant][variantKey];\n });\n const propsWithoutUndefined = props && Object.entries(props).reduce((acc, param)=>{\n let [key, value] = param;\n if (value === undefined) {\n return acc;\n }\n acc[key] = value;\n return acc;\n }, {});\n const getCompoundVariantClassNames = config === null || config === void 0 ? void 0 : (_config_compoundVariants = config.compoundVariants) === null || _config_compoundVariants === void 0 ? void 0 : _config_compoundVariants.reduce((acc, param)=>{\n let { class: cvClass, className: cvClassName, ...compoundVariantOptions } = param;\n return Object.entries(compoundVariantOptions).every((param)=>{\n let [key, value] = param;\n return Array.isArray(value) ? value.includes({\n ...defaultVariants,\n ...propsWithoutUndefined\n }[key]) : ({\n ...defaultVariants,\n ...propsWithoutUndefined\n })[key] === value;\n }) ? [\n ...acc,\n cvClass,\n cvClassName\n ] : acc;\n }, []);\n return cx(base, getVariantClassNames, getCompoundVariantClassNames, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);\n };\n\n","import { clsx, type ClassValue } from 'clsx';\n\n/**\n * Merge a list of class-name inputs into a single string.\n *\n * `clsx`-only by design (Decision D9): pharos-react no longer ships Tailwind\n * classes, so the previous `twMerge` step was wrapping each call with a\n * Tailwind-conflict resolver that nothing in the bundle benefited from.\n *\n * Consumers that build with Tailwind and want the conflict-resolution\n * behavior should keep using their own `tailwind-merge` helper alongside\n * Pharos components — `<Button className={twMerge(myClasses)} />` works\n * because Pharos-side class names are CSS Modules hashes that no Tailwind\n * resolver will ever try to deduplicate.\n */\nexport function cn(...inputs: ClassValue[]): string {\n return clsx(inputs);\n}\n","/* Button.module.css\n *\n * Per-component CSS Modules definition (Decision D9). The build emits hashed\n * class names (e.g. `Button_button__a3f2`) into dist/styles.css; the runtime\n * JS references those names through the `styles` object imported by Button.tsx.\n *\n * Every visual value comes from a `--pharos-*` token. No hex, no raw px/rem\n * for design dimensions, no Tailwind. Hover / active / focus states are\n * authored with native CSS pseudo-classes — no preprocessor, no utility-first\n * shorthand, no framework dependency at runtime.\n *\n * Consumers do not need Tailwind (any version) or any other CSS framework to\n * use this component. They only need to load the published `dist/styles.css`\n * once and the consumer's app must define `--pharos-*` (achieved by importing\n * `@code-sherpas/pharos-tokens/css`, which is a peer dependency).\n */\n\n.button {\n /* Strip native button rendering. The component is fully custom-styled,\n * so platform-specific drawing (rounded corners, shadows, focus rings\n * on macOS, push-button gradient on iOS) only adds variance without\n * adding value. `appearance: none` keeps every consumer rendering the\n * exact same pixel set. The `-webkit-appearance` line is a no-op on\n * modern Chromium but stays for older WebKit. */\n appearance: none;\n -webkit-appearance: none;\n\n /* === Box model ===\n * Explicit so the rendered control matches the height set in the size\n * rules even when the consumer's app does not ship a CSS reset. With\n * the consumer-side preflight gone, `border-box`, `margin: 0` and the\n * zero block-padding neutralise the user-agent's `padding: 1px 6px`\n * (Chrome) or `padding: 0 8px` (Firefox) on `<button>`. */\n box-sizing: border-box;\n margin: 0;\n padding-top: 0;\n padding-bottom: 0;\n border: 0 solid transparent;\n border-radius: var(--pharos-radius-full);\n\n /* === Layout === */\n display: inline-flex;\n align-items: center;\n justify-content: center;\n white-space: nowrap;\n\n /* === Optical text centering ===\n * `text-box-trim` strips the leading above the cap-line and below the\n * alphabetic baseline so `align-items: center` centres the actual\n * cap-block instead of the line-box (which includes per-font half-\n * leading that biases the visual centre upward or downward depending\n * on the font's vertical metrics). Without this, fonts with a high\n * baseline like Outfit (the D8 sans) render visibly above the\n * geometric centre even though `align-items: center` is correctly\n * applied — matching what Chromatic flagged on the migration PR.\n * The `text-box-edge` longhand picks the cap-line at the start and\n * the alphabetic baseline at the end so descenders (\"y\", \"g\", ...)\n * still hang below the cap-block as the font intended; only the\n * extra leading is trimmed.\n *\n * Browser support: Chromium 133+ / Safari 18.2+ ship the property,\n * Firefox has it behind a flag at the time of writing. In engines\n * without support the declaration is ignored and the text falls\n * back to standard line-box centring — which is the same behaviour\n * the bundle had before this rule, so the fallback is safe. */\n text-box-trim: trim-both;\n text-box-edge: cap alphabetic;\n\n /* === Typography ===\n * Each font longhand is set explicitly. The shorthand `font: inherit`\n * was tempting but the CSS spec says the `font` shorthand resets a\n * dozen related properties (`font-feature-settings`,\n * `font-variation-settings`, `font-kerning`, `font-optical-sizing`,\n * `font-size-adjust`, etc.) to their *initial* values rather than\n * inheriting them. That subtle reset shifted the rendered baseline\n * by a fraction of a pixel against the consumer's surrounding text,\n * which Chromatic flagged as a diff vs 0.2.x. The size variants\n * below override `font-size` and `line-height` per size. */\n font-family: var(--pharos-font-family-sans);\n font-style: normal;\n font-weight: var(--pharos-font-weight-medium);\n font-stretch: normal;\n font-variant: normal;\n font-kerning: auto;\n font-optical-sizing: auto;\n font-size-adjust: none;\n font-feature-settings: normal;\n font-variation-settings: normal;\n letter-spacing: normal;\n text-decoration: none;\n text-transform: none;\n /* Default size (overridden per size variant) — keeps a sensible\n * fallback if a Button is somehow rendered without a size class. */\n font-size: var(--pharos-font-size-sm);\n line-height: 1.5;\n\n /* === Interactivity === */\n user-select: none;\n cursor: pointer;\n background-color: transparent;\n color: inherit;\n\n /* === Transitions === */\n transition-property: color, background-color, border-color, box-shadow;\n transition-duration: var(--pharos-duration-normal);\n transition-timing-function: var(--pharos-easing-out);\n}\n\n.button:focus-visible {\n outline: none;\n /* Two-stop box-shadow mimics the Tailwind `ring-offset-2 ring-2` pattern:\n * an inner halo in the surface color separates the ring from the button\n * fill, and the outer color is the per-intent focus accent. */\n box-shadow:\n 0 0 0 2px var(--pharos-color-base-white),\n 0 0 0 4px var(--button-focus-ring, var(--pharos-color-primary-600));\n}\n\n.button:disabled {\n pointer-events: none;\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* === Intents === */\n\n.primary {\n background-color: var(--pharos-color-neutral-900);\n color: var(--pharos-color-base-white);\n --button-focus-ring: var(--pharos-color-primary-600);\n}\n\n.primary:hover {\n background-color: var(--pharos-color-neutral-700);\n}\n\n.primary:active {\n background-color: var(--pharos-color-neutral-950);\n}\n\n.secondary {\n background-color: var(--pharos-color-base-white);\n color: var(--pharos-color-neutral-900);\n border: 1px solid var(--pharos-color-neutral-200);\n --button-focus-ring: var(--pharos-color-primary-600);\n}\n\n.secondary:hover {\n background-color: var(--pharos-color-neutral-50);\n border-color: var(--pharos-color-neutral-300);\n}\n\n.secondary:active {\n background-color: var(--pharos-color-neutral-100);\n}\n\n.ghost {\n background-color: transparent;\n color: var(--pharos-color-neutral-900);\n --button-focus-ring: var(--pharos-color-primary-600);\n}\n\n.ghost:hover {\n background-color: var(--pharos-color-neutral-100);\n}\n\n.ghost:active {\n background-color: var(--pharos-color-neutral-200);\n}\n\n.destructive {\n background-color: var(--pharos-color-semantic-error-fg);\n color: var(--pharos-color-semantic-error-on);\n --button-focus-ring: var(--pharos-color-semantic-error-fg);\n}\n\n.destructive:hover {\n background-color: var(--pharos-color-semantic-error-fg-hover);\n}\n\n.destructive:active {\n background-color: var(--pharos-color-semantic-error-fg-active);\n}\n\n/* === Sizes ===\n *\n * Heights and paddings are derived from `--pharos-spacing-*` so a runtime\n * override of the spacing scale rescales the whole control proportionally.\n * Font sizes use `--pharos-font-size-*` tokens; the line-height ratios match\n * the Tailwind v4 defaults that shipped in 0.2.x so visual fidelity is\n * preserved across the migration. */\n\n.sizeSm {\n height: var(--pharos-spacing-8);\n gap: var(--pharos-spacing-1-5);\n padding: 0 var(--pharos-spacing-3);\n font-size: var(--pharos-font-size-sm);\n line-height: calc(1.25 / 0.875);\n}\n\n.sizeMd {\n height: var(--pharos-spacing-10);\n gap: var(--pharos-spacing-2);\n padding: 0 var(--pharos-spacing-4);\n font-size: var(--pharos-font-size-sm);\n line-height: calc(1.25 / 0.875);\n}\n\n.sizeLg {\n height: var(--pharos-spacing-12);\n gap: var(--pharos-spacing-2);\n padding: 0 var(--pharos-spacing-6);\n font-size: var(--pharos-font-size-base);\n line-height: 1.5;\n}\n","import { useRender } from '@base-ui/react/use-render';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../lib/cn';\nimport styles from './Button.module.css';\n\n/**\n * Maps the two public variant axes (`intent`, `size`) to the per-class names\n * inside `Button.module.css`. CVA stays as the typed-variants mechanism\n * (Decision D9 keeps the authoring DX) but each value points at a hashed\n * CSS Modules class instead of a Tailwind utility string. The base class\n * carries layout, typography and the focus-visible ring — every variant\n * stacks on top of it.\n */\nconst buttonVariants = cva(styles.button, {\n variants: {\n intent: {\n // `primary` is the most emphasized CTA. It renders in neutral-900,\n // matching Alexandria's `NewButton variant=\"filled\" tone=\"default\"`\n // and the Linear/Vercel/Notion convention of \"brand color as accent,\n // dark neutral as primary action\". The blue `primary-*` palette is\n // reserved for brand accents (focus rings, highlights, badges) —\n // not for the default filled button.\n primary: styles.primary,\n secondary: styles.secondary,\n ghost: styles.ghost,\n destructive: styles.destructive,\n },\n size: {\n sm: styles.sizeSm,\n md: styles.sizeMd,\n lg: styles.sizeLg,\n },\n },\n defaultVariants: {\n intent: 'primary',\n size: 'md',\n },\n});\n\ntype ButtonVariantProps = VariantProps<typeof buttonVariants>;\n\n/**\n * Props accepted by `<Button>`. Extends Base UI's `useRender.ComponentProps<'button'>`,\n * which means the component accepts every native `<button>` attribute plus the\n * `render` prop for composition:\n *\n * ```tsx\n * // Render as a router link (inherits all Button styles and behavior):\n * <Button render={<Link to=\"/dashboard\" />}>Dashboard</Button>\n * // Or as a function receiving props + state:\n * <Button render={(props) => <a {...props} href=\"/x\" />}>External</Button>\n * ```\n */\nexport interface ButtonProps extends useRender.ComponentProps<'button'>, ButtonVariantProps {}\n\nexport function Button({\n render,\n className,\n intent,\n size,\n type = 'button',\n ref,\n ...rest\n}: ButtonProps) {\n const resolvedIntent = intent ?? 'primary';\n const resolvedSize = size ?? 'md';\n return useRender({\n render: render ?? <button />,\n ref,\n defaultTagName: 'button',\n props: {\n type,\n // `data-pharos-intent` / `data-pharos-size` are stable hooks for\n // selectors and tests. They mirror the CSS Modules class but stay\n // semantic — consumer queries do not need to know the hashed class\n // name and tests do not need to import the module to assert variant.\n 'data-pharos-intent': resolvedIntent,\n 'data-pharos-size': resolvedSize,\n className: cn(buttonVariants({ intent: resolvedIntent, size: resolvedSize }), className),\n ...rest,\n },\n });\n}\n\nButton.displayName = 'Button';\n\nexport { buttonVariants };\n","/* PharosHello.module.css\n *\n * Demo / smoke component. Per Decision D9, every component owns its CSS\n * Modules file alongside the .tsx; build emits hashed class names into\n * dist/styles.css. Values come from `--pharos-*` tokens.\n */\n\n.hello {\n border-radius: var(--pharos-radius-lg);\n background-color: var(--pharos-color-primary-600);\n color: var(--pharos-color-base-white);\n padding: var(--pharos-spacing-2) var(--pharos-spacing-4);\n font-family: var(--pharos-font-family-sans);\n /* Tracked alongside the original Tailwind story-only shadow so the smoke\n * demo matches its previous visual snapshot. */\n box-shadow: 0 1px 2px rgb(0 0 0 / 0.08);\n}\n","import type { HTMLAttributes } from 'react';\nimport { cn } from '../lib/cn';\nimport styles from './PharosHello.module.css';\n\nexport interface PharosHelloProps extends HTMLAttributes<HTMLDivElement> {\n name?: string;\n}\n\nexport function PharosHello({ name = 'world', className, ...rest }: PharosHelloProps) {\n return (\n <div className={cn(styles.hello, className)} {...rest}>\n Pharos says hello, {name}.\n </div>\n );\n}\n"],"x_google_ignoreList":[0,1],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs","../node_modules/.pnpm/class-variance-authority@0.7.1/node_modules/class-variance-authority/dist/index.mjs","../src/lib/cn.ts","../src/components/Button.module.css","../src/components/Button.tsx","../src/components/Badge.module.css","../src/components/Badge.tsx"],"sourcesContent":["function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","/**\n * Copyright 2022 Joe Bell. All rights reserved.\n *\n * This file is licensed to you under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with the\n * License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */ import { clsx } from \"clsx\";\nconst falsyToString = (value)=>typeof value === \"boolean\" ? `${value}` : value === 0 ? \"0\" : value;\nexport const cx = clsx;\nexport const cva = (base, config)=>(props)=>{\n var _config_compoundVariants;\n if ((config === null || config === void 0 ? void 0 : config.variants) == null) return cx(base, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);\n const { variants, defaultVariants } = config;\n const getVariantClassNames = Object.keys(variants).map((variant)=>{\n const variantProp = props === null || props === void 0 ? void 0 : props[variant];\n const defaultVariantProp = defaultVariants === null || defaultVariants === void 0 ? void 0 : defaultVariants[variant];\n if (variantProp === null) return null;\n const variantKey = falsyToString(variantProp) || falsyToString(defaultVariantProp);\n return variants[variant][variantKey];\n });\n const propsWithoutUndefined = props && Object.entries(props).reduce((acc, param)=>{\n let [key, value] = param;\n if (value === undefined) {\n return acc;\n }\n acc[key] = value;\n return acc;\n }, {});\n const getCompoundVariantClassNames = config === null || config === void 0 ? void 0 : (_config_compoundVariants = config.compoundVariants) === null || _config_compoundVariants === void 0 ? void 0 : _config_compoundVariants.reduce((acc, param)=>{\n let { class: cvClass, className: cvClassName, ...compoundVariantOptions } = param;\n return Object.entries(compoundVariantOptions).every((param)=>{\n let [key, value] = param;\n return Array.isArray(value) ? value.includes({\n ...defaultVariants,\n ...propsWithoutUndefined\n }[key]) : ({\n ...defaultVariants,\n ...propsWithoutUndefined\n })[key] === value;\n }) ? [\n ...acc,\n cvClass,\n cvClassName\n ] : acc;\n }, []);\n return cx(base, getVariantClassNames, getCompoundVariantClassNames, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);\n };\n\n","import { clsx, type ClassValue } from 'clsx';\n\n/**\n * Merge a list of class-name inputs into a single string.\n *\n * `clsx`-only by design (Decision D9): pharos-react no longer ships Tailwind\n * classes, so the previous `twMerge` step was wrapping each call with a\n * Tailwind-conflict resolver that nothing in the bundle benefited from.\n *\n * Consumers that build with Tailwind and want the conflict-resolution\n * behavior should keep using their own `tailwind-merge` helper alongside\n * Pharos components — `<Button className={twMerge(myClasses)} />` works\n * because Pharos-side class names are CSS Modules hashes that no Tailwind\n * resolver will ever try to deduplicate.\n */\nexport function cn(...inputs: ClassValue[]): string {\n return clsx(inputs);\n}\n","/* Button.module.css\n *\n * Per-component CSS Modules definition (Decision D9). The build emits hashed\n * class names (e.g. `Button_button__a3f2`) into dist/styles.css; the runtime\n * JS references those names through the `styles` object imported by Button.tsx.\n *\n * Every visual value comes from a `--pharos-*` token. No hex, no raw px/rem\n * for design dimensions, no Tailwind. Hover / active / focus states are\n * authored with native CSS pseudo-classes — no preprocessor, no utility-first\n * shorthand, no framework dependency at runtime.\n *\n * Consumers do not need Tailwind (any version) or any other CSS framework to\n * use this component. They only need to load the published `dist/styles.css`\n * once and the consumer's app must define `--pharos-*` (achieved by importing\n * `@code-sherpas/pharos-tokens/css`, which is a peer dependency).\n */\n\n.button {\n /* Strip native button rendering. The component is fully custom-styled,\n * so platform-specific drawing (rounded corners, shadows, focus rings\n * on macOS, push-button gradient on iOS) only adds variance without\n * adding value. `appearance: none` keeps every consumer rendering the\n * exact same pixel set. The `-webkit-appearance` line is a no-op on\n * modern Chromium but stays for older WebKit. */\n appearance: none;\n -webkit-appearance: none;\n\n /* === Box model ===\n * Explicit so the rendered control matches the height set in the size\n * rules even when the consumer's app does not ship a CSS reset. With\n * the consumer-side preflight gone, `border-box`, `margin: 0` and the\n * zero block-padding neutralise the user-agent's `padding: 1px 6px`\n * (Chrome) or `padding: 0 8px` (Firefox) on `<button>`. */\n box-sizing: border-box;\n margin: 0;\n padding-top: 0;\n padding-bottom: 0;\n border: 0 solid transparent;\n border-radius: var(--pharos-radius-full);\n\n /* === Layout === */\n display: inline-flex;\n align-items: center;\n justify-content: center;\n white-space: nowrap;\n\n /* === Optical text centering ===\n * `text-box-trim` strips the leading above the cap-line and below the\n * alphabetic baseline so `align-items: center` centres the actual\n * cap-block instead of the line-box (which includes per-font half-\n * leading that biases the visual centre upward or downward depending\n * on the font's vertical metrics). Without this, fonts with a high\n * baseline like Outfit (the D8 sans) render visibly above the\n * geometric centre even though `align-items: center` is correctly\n * applied — matching what Chromatic flagged on the migration PR.\n * The `text-box-edge` longhand picks the cap-line at the start and\n * the alphabetic baseline at the end so descenders (\"y\", \"g\", ...)\n * still hang below the cap-block as the font intended; only the\n * extra leading is trimmed.\n *\n * Browser support: Chromium 133+ / Safari 18.2+ ship the property,\n * Firefox has it behind a flag at the time of writing. In engines\n * without support the declaration is ignored and the text falls\n * back to standard line-box centring — which is the same behaviour\n * the bundle had before this rule, so the fallback is safe. */\n text-box-trim: trim-both;\n text-box-edge: cap alphabetic;\n\n /* === Typography ===\n * Each font longhand is set explicitly. The shorthand `font: inherit`\n * was tempting but the CSS spec says the `font` shorthand resets a\n * dozen related properties (`font-feature-settings`,\n * `font-variation-settings`, `font-kerning`, `font-optical-sizing`,\n * `font-size-adjust`, etc.) to their *initial* values rather than\n * inheriting them. That subtle reset shifted the rendered baseline\n * by a fraction of a pixel against the consumer's surrounding text,\n * which Chromatic flagged as a diff vs 0.2.x. The size variants\n * below override `font-size` and `line-height` per size. */\n font-family: var(--pharos-font-family-sans);\n font-style: normal;\n font-weight: var(--pharos-font-weight-medium);\n font-stretch: normal;\n font-variant: normal;\n font-kerning: auto;\n font-optical-sizing: auto;\n font-size-adjust: none;\n font-feature-settings: normal;\n font-variation-settings: normal;\n letter-spacing: normal;\n text-decoration: none;\n text-transform: none;\n /* Default size (overridden per size variant) — keeps a sensible\n * fallback if a Button is somehow rendered without a size class. */\n font-size: var(--pharos-font-size-sm);\n line-height: 1.5;\n\n /* === Interactivity === */\n user-select: none;\n cursor: pointer;\n background-color: transparent;\n color: inherit;\n\n /* === Transitions === */\n transition-property: color, background-color, border-color, box-shadow;\n transition-duration: var(--pharos-duration-normal);\n transition-timing-function: var(--pharos-easing-out);\n}\n\n.button:focus-visible {\n outline: none;\n /* Two-stop box-shadow mimics the Tailwind `ring-offset-2 ring-2` pattern:\n * an inner halo in the surface color separates the ring from the button\n * fill, and the outer color is the per-intent focus accent. */\n box-shadow:\n 0 0 0 2px var(--pharos-color-base-white),\n 0 0 0 4px var(--button-focus-ring, var(--pharos-color-primary-600));\n}\n\n.button:disabled {\n pointer-events: none;\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* === Intents === */\n\n.primary {\n background-color: var(--pharos-color-neutral-900);\n color: var(--pharos-color-base-white);\n --button-focus-ring: var(--pharos-color-primary-600);\n}\n\n.primary:hover {\n background-color: var(--pharos-color-neutral-700);\n}\n\n.primary:active {\n background-color: var(--pharos-color-neutral-950);\n}\n\n.secondary {\n background-color: var(--pharos-color-base-white);\n color: var(--pharos-color-neutral-900);\n border: 1px solid var(--pharos-color-neutral-200);\n --button-focus-ring: var(--pharos-color-primary-600);\n}\n\n.secondary:hover {\n background-color: var(--pharos-color-neutral-50);\n border-color: var(--pharos-color-neutral-300);\n}\n\n.secondary:active {\n background-color: var(--pharos-color-neutral-100);\n}\n\n.ghost {\n background-color: transparent;\n color: var(--pharos-color-neutral-900);\n --button-focus-ring: var(--pharos-color-primary-600);\n}\n\n.ghost:hover {\n background-color: var(--pharos-color-neutral-100);\n}\n\n.ghost:active {\n background-color: var(--pharos-color-neutral-200);\n}\n\n.destructive {\n background-color: var(--pharos-color-semantic-error-fg);\n color: var(--pharos-color-semantic-error-on);\n --button-focus-ring: var(--pharos-color-semantic-error-fg);\n}\n\n.destructive:hover {\n background-color: var(--pharos-color-semantic-error-fg-hover);\n}\n\n.destructive:active {\n background-color: var(--pharos-color-semantic-error-fg-active);\n}\n\n/* === Sizes ===\n *\n * Heights and paddings are derived from `--pharos-spacing-*` so a runtime\n * override of the spacing scale rescales the whole control proportionally.\n * Font sizes use `--pharos-font-size-*` tokens; the line-height ratios match\n * the Tailwind v4 defaults that shipped in 0.2.x so visual fidelity is\n * preserved across the migration. */\n\n.sizeSm {\n height: var(--pharos-spacing-8);\n gap: var(--pharos-spacing-1-5);\n padding: 0 var(--pharos-spacing-3);\n font-size: var(--pharos-font-size-sm);\n line-height: calc(1.25 / 0.875);\n}\n\n.sizeMd {\n height: var(--pharos-spacing-10);\n gap: var(--pharos-spacing-2);\n padding: 0 var(--pharos-spacing-4);\n font-size: var(--pharos-font-size-sm);\n line-height: calc(1.25 / 0.875);\n}\n\n.sizeLg {\n height: var(--pharos-spacing-12);\n gap: var(--pharos-spacing-2);\n padding: 0 var(--pharos-spacing-6);\n font-size: var(--pharos-font-size-base);\n line-height: 1.5;\n}\n","import { useRender } from '@base-ui/react/use-render';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../lib/cn';\nimport styles from './Button.module.css';\n\n/**\n * Maps the two public variant axes (`intent`, `size`) to the per-class names\n * inside `Button.module.css`. CVA stays as the typed-variants mechanism\n * (Decision D9 keeps the authoring DX) but each value points at a hashed\n * CSS Modules class instead of a Tailwind utility string. The base class\n * carries layout, typography and the focus-visible ring — every variant\n * stacks on top of it.\n */\nconst buttonVariants = cva(styles.button, {\n variants: {\n intent: {\n // `primary` is the most emphasized CTA. It renders in neutral-900,\n // matching Alexandria's `NewButton variant=\"filled\" tone=\"default\"`\n // and the Linear/Vercel/Notion convention of \"brand color as accent,\n // dark neutral as primary action\". The blue `primary-*` palette is\n // reserved for brand accents (focus rings, highlights, badges) —\n // not for the default filled button.\n primary: styles.primary,\n secondary: styles.secondary,\n ghost: styles.ghost,\n destructive: styles.destructive,\n },\n size: {\n sm: styles.sizeSm,\n md: styles.sizeMd,\n lg: styles.sizeLg,\n },\n },\n defaultVariants: {\n intent: 'primary',\n size: 'md',\n },\n});\n\ntype ButtonVariantProps = VariantProps<typeof buttonVariants>;\n\n/**\n * Props accepted by `<Button>`. Extends Base UI's `useRender.ComponentProps<'button'>`,\n * which means the component accepts every native `<button>` attribute plus the\n * `render` prop for composition:\n *\n * ```tsx\n * // Render as a router link (inherits all Button styles and behavior):\n * <Button render={<Link to=\"/dashboard\" />}>Dashboard</Button>\n * // Or as a function receiving props + state:\n * <Button render={(props) => <a {...props} href=\"/x\" />}>External</Button>\n * ```\n */\nexport interface ButtonProps extends useRender.ComponentProps<'button'>, ButtonVariantProps {}\n\nexport function Button({\n render,\n className,\n intent,\n size,\n type = 'button',\n ref,\n ...rest\n}: ButtonProps) {\n const resolvedIntent = intent ?? 'primary';\n const resolvedSize = size ?? 'md';\n return useRender({\n render: render ?? <button />,\n ref,\n defaultTagName: 'button',\n props: {\n type,\n // `data-pharos-intent` / `data-pharos-size` are stable hooks for\n // selectors and tests. They mirror the CSS Modules class but stay\n // semantic — consumer queries do not need to know the hashed class\n // name and tests do not need to import the module to assert variant.\n 'data-pharos-intent': resolvedIntent,\n 'data-pharos-size': resolvedSize,\n className: cn(buttonVariants({ intent: resolvedIntent, size: resolvedSize }), className),\n ...rest,\n },\n });\n}\n\nButton.displayName = 'Button';\n\nexport { buttonVariants };\n","/* Base layout. Same across every variant — only surface, label and border\n * change. Inline-flex so a child SVG (rendered by the consumer as plain\n * children) sits on the same baseline as the label. The font-relative SVG\n * sizing rule mirrors shadcn's `[&>svg]:size-3` utility. */\n.badge {\n display: inline-flex;\n align-items: center;\n gap: var(--pharos-spacing-1);\n padding: 0 var(--pharos-spacing-2);\n height: var(--pharos-spacing-5);\n border: 1px solid transparent;\n border-radius: var(--pharos-radius-md);\n font-family: var(--pharos-font-family-sans);\n font-size: var(--pharos-font-size-xs);\n font-weight: var(--pharos-font-weight-medium);\n line-height: 1;\n white-space: nowrap;\n width: fit-content;\n text-box-trim: trim-both;\n text-box-edge: cap alphabetic;\n}\n\n.badge > svg {\n width: 0.75em;\n height: 0.75em;\n flex: 0 0 auto;\n pointer-events: none;\n}\n\n.default {\n background: var(--pharos-color-neutral-900);\n color: var(--pharos-color-base-white);\n}\n\n.secondary {\n background: var(--pharos-color-neutral-100);\n color: var(--pharos-color-neutral-900);\n}\n\n.destructive {\n background: var(--pharos-color-semantic-error-fg);\n color: var(--pharos-color-semantic-error-on);\n}\n\n.outline {\n background: transparent;\n color: var(--pharos-color-neutral-900);\n border-color: var(--pharos-color-neutral-300);\n}\n\n.success {\n background: var(--pharos-color-semantic-success-fg);\n color: var(--pharos-color-semantic-success-on);\n}\n\n.warning {\n background: var(--pharos-color-semantic-warning-fg);\n color: var(--pharos-color-semantic-warning-on);\n}\n\n.info {\n background: var(--pharos-color-semantic-info-fg);\n color: var(--pharos-color-semantic-info-on);\n}\n","import type { ComponentPropsWithoutRef } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../lib/cn';\nimport styles from './Badge.module.css';\n\n/**\n * Maps the `variant` axis to per-class names inside `Badge.module.css`.\n * The variant set follows shadcn/ui — `default`, `secondary`, `destructive`,\n * `outline` — extended with the semantic tones every modern DS exposes\n * (`success`, `warning`, `info`). Naming stays asymmetric on purpose:\n * `destructive` is the canonical shadcn name for \"negative tone\" and we\n * keep it instead of renaming it to `error`, so consumers familiar with\n * shadcn pick the right value without re-learning the API.\n */\nconst badgeVariants = cva(styles.badge, {\n variants: {\n variant: {\n default: styles.default,\n secondary: styles.secondary,\n destructive: styles.destructive,\n outline: styles.outline,\n success: styles.success,\n warning: styles.warning,\n info: styles.info,\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n});\n\ntype BadgeVariantProps = VariantProps<typeof badgeVariants>;\n\nexport interface BadgeProps extends ComponentPropsWithoutRef<'span'>, BadgeVariantProps {}\n\nexport function Badge({ variant, className, ...rest }: BadgeProps) {\n const resolvedVariant = variant ?? 'default';\n return (\n <span\n data-pharos-variant={resolvedVariant}\n className={cn(badgeVariants({ variant: resolvedVariant }), className)}\n {...rest}\n />\n );\n}\n\nBadge.displayName = 'Badge';\n\nexport { badgeVariants };\n"],"x_google_ignoreList":[0,1],"mappings":"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"}
package/dist/styles.css CHANGED
@@ -1,2 +1,2 @@
1
- ._button_6sqxz_18{appearance:none;box-sizing:border-box;border-radius:var(--pharos-radius-full);white-space:nowrap;text-box-trim:trim-both;text-box-edge:cap alphabetic;font-family:var(--pharos-font-family-sans);font-style:normal;font-weight:var(--pharos-font-weight-medium);font-variant:normal;font-kerning:auto;font-optical-sizing:auto;font-size-adjust:none;font-feature-settings:normal;font-variation-settings:normal;letter-spacing:normal;text-transform:none;font-stretch:100%;font-size:var(--pharos-font-size-sm);-webkit-user-select:none;user-select:none;cursor:pointer;color:inherit;transition-property:color,background-color,border-color,box-shadow;transition-duration:var(--pharos-duration-normal);transition-timing-function:var(--pharos-easing-out);background-color:#0000;border:0 solid #0000;justify-content:center;align-items:center;margin:0;padding-top:0;padding-bottom:0;line-height:1.5;text-decoration:none;display:inline-flex}._button_6sqxz_18:focus-visible{box-shadow:0 0 0 2px var(--pharos-color-base-white), 0 0 0 4px var(--button-focus-ring,var(--pharos-color-primary-600));outline:none}._button_6sqxz_18:disabled{pointer-events:none;cursor:not-allowed;opacity:.5}._primary_6sqxz_127{background-color:var(--pharos-color-neutral-900);color:var(--pharos-color-base-white);--button-focus-ring:var(--pharos-color-primary-600)}._primary_6sqxz_127:hover{background-color:var(--pharos-color-neutral-700)}._primary_6sqxz_127:active{background-color:var(--pharos-color-neutral-950)}._secondary_6sqxz_141{background-color:var(--pharos-color-base-white);color:var(--pharos-color-neutral-900);border:1px solid var(--pharos-color-neutral-200);--button-focus-ring:var(--pharos-color-primary-600)}._secondary_6sqxz_141:hover{background-color:var(--pharos-color-neutral-50);border-color:var(--pharos-color-neutral-300)}._secondary_6sqxz_141:active{background-color:var(--pharos-color-neutral-100)}._ghost_6sqxz_157{color:var(--pharos-color-neutral-900);--button-focus-ring:var(--pharos-color-primary-600);background-color:#0000}._ghost_6sqxz_157:hover{background-color:var(--pharos-color-neutral-100)}._ghost_6sqxz_157:active{background-color:var(--pharos-color-neutral-200)}._destructive_6sqxz_171{background-color:var(--pharos-color-semantic-error-fg);color:var(--pharos-color-semantic-error-on);--button-focus-ring:var(--pharos-color-semantic-error-fg)}._destructive_6sqxz_171:hover{background-color:var(--pharos-color-semantic-error-fg-hover)}._destructive_6sqxz_171:active{background-color:var(--pharos-color-semantic-error-fg-active)}._sizeSm_6sqxz_193{height:var(--pharos-spacing-8);gap:var(--pharos-spacing-1-5);padding:0 var(--pharos-spacing-3);font-size:var(--pharos-font-size-sm);line-height:1.42857}._sizeMd_6sqxz_201{height:var(--pharos-spacing-10);gap:var(--pharos-spacing-2);padding:0 var(--pharos-spacing-4);font-size:var(--pharos-font-size-sm);line-height:1.42857}._sizeLg_6sqxz_209{height:var(--pharos-spacing-12);gap:var(--pharos-spacing-2);padding:0 var(--pharos-spacing-6);font-size:var(--pharos-font-size-base);line-height:1.5}._hello_13wdd_8{border-radius:var(--pharos-radius-lg);background-color:var(--pharos-color-primary-600);color:var(--pharos-color-base-white);padding:var(--pharos-spacing-2) var(--pharos-spacing-4);font-family:var(--pharos-font-family-sans);box-shadow:0 1px 2px #00000014}
1
+ ._button_6sqxz_18{appearance:none;box-sizing:border-box;border-radius:var(--pharos-radius-full);white-space:nowrap;text-box-trim:trim-both;text-box-edge:cap alphabetic;font-family:var(--pharos-font-family-sans);font-style:normal;font-weight:var(--pharos-font-weight-medium);font-variant:normal;font-kerning:auto;font-optical-sizing:auto;font-size-adjust:none;font-feature-settings:normal;font-variation-settings:normal;letter-spacing:normal;text-transform:none;font-stretch:100%;font-size:var(--pharos-font-size-sm);-webkit-user-select:none;user-select:none;cursor:pointer;color:inherit;transition-property:color,background-color,border-color,box-shadow;transition-duration:var(--pharos-duration-normal);transition-timing-function:var(--pharos-easing-out);background-color:#0000;border:0 solid #0000;justify-content:center;align-items:center;margin:0;padding-top:0;padding-bottom:0;line-height:1.5;text-decoration:none;display:inline-flex}._button_6sqxz_18:focus-visible{box-shadow:0 0 0 2px var(--pharos-color-base-white), 0 0 0 4px var(--button-focus-ring,var(--pharos-color-primary-600));outline:none}._button_6sqxz_18:disabled{pointer-events:none;cursor:not-allowed;opacity:.5}._primary_6sqxz_127{background-color:var(--pharos-color-neutral-900);color:var(--pharos-color-base-white);--button-focus-ring:var(--pharos-color-primary-600)}._primary_6sqxz_127:hover{background-color:var(--pharos-color-neutral-700)}._primary_6sqxz_127:active{background-color:var(--pharos-color-neutral-950)}._secondary_6sqxz_141{background-color:var(--pharos-color-base-white);color:var(--pharos-color-neutral-900);border:1px solid var(--pharos-color-neutral-200);--button-focus-ring:var(--pharos-color-primary-600)}._secondary_6sqxz_141:hover{background-color:var(--pharos-color-neutral-50);border-color:var(--pharos-color-neutral-300)}._secondary_6sqxz_141:active{background-color:var(--pharos-color-neutral-100)}._ghost_6sqxz_157{color:var(--pharos-color-neutral-900);--button-focus-ring:var(--pharos-color-primary-600);background-color:#0000}._ghost_6sqxz_157:hover{background-color:var(--pharos-color-neutral-100)}._ghost_6sqxz_157:active{background-color:var(--pharos-color-neutral-200)}._destructive_6sqxz_171{background-color:var(--pharos-color-semantic-error-fg);color:var(--pharos-color-semantic-error-on);--button-focus-ring:var(--pharos-color-semantic-error-fg)}._destructive_6sqxz_171:hover{background-color:var(--pharos-color-semantic-error-fg-hover)}._destructive_6sqxz_171:active{background-color:var(--pharos-color-semantic-error-fg-active)}._sizeSm_6sqxz_193{height:var(--pharos-spacing-8);gap:var(--pharos-spacing-1-5);padding:0 var(--pharos-spacing-3);font-size:var(--pharos-font-size-sm);line-height:1.42857}._sizeMd_6sqxz_201{height:var(--pharos-spacing-10);gap:var(--pharos-spacing-2);padding:0 var(--pharos-spacing-4);font-size:var(--pharos-font-size-sm);line-height:1.42857}._sizeLg_6sqxz_209{height:var(--pharos-spacing-12);gap:var(--pharos-spacing-2);padding:0 var(--pharos-spacing-6);font-size:var(--pharos-font-size-base);line-height:1.5}._badge_rqyn4_5{align-items:center;gap:var(--pharos-spacing-1);padding:0 var(--pharos-spacing-2);height:var(--pharos-spacing-5);border-radius:var(--pharos-radius-md);font-family:var(--pharos-font-family-sans);font-size:var(--pharos-font-size-xs);font-weight:var(--pharos-font-weight-medium);white-space:nowrap;text-box-trim:trim-both;text-box-edge:cap alphabetic;border:1px solid #0000;width:fit-content;line-height:1;display:inline-flex}._badge_rqyn4_5>svg{pointer-events:none;flex:none;width:.75em;height:.75em}._default_rqyn4_30{background:var(--pharos-color-neutral-900);color:var(--pharos-color-base-white)}._secondary_rqyn4_35{background:var(--pharos-color-neutral-100);color:var(--pharos-color-neutral-900)}._destructive_rqyn4_40{background:var(--pharos-color-semantic-error-fg);color:var(--pharos-color-semantic-error-on)}._outline_rqyn4_45{color:var(--pharos-color-neutral-900);border-color:var(--pharos-color-neutral-300);background:0 0}._success_rqyn4_51{background:var(--pharos-color-semantic-success-fg);color:var(--pharos-color-semantic-success-on)}._warning_rqyn4_56{background:var(--pharos-color-semantic-warning-fg);color:var(--pharos-color-semantic-warning-on)}._info_rqyn4_61{background:var(--pharos-color-semantic-info-fg);color:var(--pharos-color-semantic-info-on)}
2
2
  /*$vite$:1*/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-sherpas/pharos-react",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "React component library for Code Sherpas. Consumes @code-sherpas/pharos-tokens. Vite library mode + CSS Modules — no Tailwind, no CSS framework required at the consumer.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -33,6 +33,9 @@
33
33
  "engines": {
34
34
  "node": ">=22"
35
35
  },
36
+ "lint-staged": {
37
+ "*.{ts,tsx,js,mjs,cjs,json,md,mdx,css,yml,yaml}": "prettier --write"
38
+ },
36
39
  "peerDependencies": {
37
40
  "@base-ui/react": "^1.4.1",
38
41
  "@code-sherpas/pharos-tokens": "^0.4.0",
@@ -66,7 +69,9 @@
66
69
  "eslint-config-prettier": "^10.1.8",
67
70
  "eslint-plugin-react-hooks": "^7.1.1",
68
71
  "eslint-plugin-react-refresh": "^0.5.2",
72
+ "husky": "^9.1.7",
69
73
  "jsdom": "^29.0.2",
74
+ "lint-staged": "^16.4.0",
70
75
  "prettier": "^3.4.2",
71
76
  "react": "^19.2.0",
72
77
  "react-dom": "^19.2.0",
@@ -1,6 +0,0 @@
1
- import { HTMLAttributes } from 'react';
2
- export interface PharosHelloProps extends HTMLAttributes<HTMLDivElement> {
3
- name?: string;
4
- }
5
- export declare function PharosHello({ name, className, ...rest }: PharosHelloProps): import("react/jsx-runtime").JSX.Element;
6
- //# sourceMappingURL=PharosHello.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PharosHello.d.ts","sourceRoot":"","sources":["../../src/components/PharosHello.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAI5C,MAAM,WAAW,gBAAiB,SAAQ,cAAc,CAAC,cAAc,CAAC;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAc,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,gBAAgB,2CAMnF"}