@carbonbits/sixr 1.0.4 → 1.2.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
@@ -1,7 +1,164 @@
1
- ## Sixr
1
+ # Sixr
2
2
 
3
- Component library for carbonbits.
3
+ E-commerce geared React component library for [carbonbits](https://carbonbits.co.ke/sixr).
4
4
 
5
- Contributing Guide
6
- ---
7
- Local machine shuld support nodejs
5
+ Ships ESM + CJS builds with type declarations, and a prebuilt stylesheet. Static
6
+ components are React Server Component safe and need no `"use client"` boundary in the
7
+ Next.js App Router; interactive ones carry their own directive, so either kind imports
8
+ cleanly into a server component.
9
+
10
+ ## Install
11
+
12
+ ```sh
13
+ pnpm add @carbonbits/sixr
14
+ ```
15
+
16
+ `react` and `react-dom` are peer dependencies (`^19.0.0`). `zod` (`^4`) is an optional
17
+ peer dependency, needed only for `@carbonbits/sixr/validation`.
18
+
19
+ ## Usage
20
+
21
+ Load the stylesheet once, at the root of your app:
22
+
23
+ ```tsx
24
+ // app/layout.tsx
25
+ import "@carbonbits/sixr/styles.css";
26
+ ```
27
+
28
+ Each component has its own subpath. Import from there:
29
+
30
+ ```tsx
31
+ // app/cart/page.tsx
32
+ import { Cart, type CartProduct } from "@carbonbits/sixr/cart";
33
+
34
+ const products: CartProduct[] = [
35
+ { name: "Stovetop kettle", price: 1500 },
36
+ { name: "Enamel mug", price: 500 },
37
+ ];
38
+
39
+ export default function Page() {
40
+ return <Cart products={products} currency="KES" locale="en-KE" />;
41
+ }
42
+ ```
43
+
44
+ The root barrel (`@carbonbits/sixr`) re-exports everything and works too, but the
45
+ subpath is the one to reach for.
46
+
47
+ ## Components
48
+
49
+ ### `Cart`
50
+
51
+ ```tsx
52
+ import { Cart } from "@carbonbits/sixr/cart";
53
+ ```
54
+
55
+ | Prop | Type | Default | Description |
56
+ | ---------- | --------------- | --------- | --------------------------------------- |
57
+ | `products` | `CartProduct[]` | `[]` | Lines to render. Empty shows a placeholder. |
58
+ | `currency` | `string` | `"KES"` | ISO 4217 code used to format prices. |
59
+ | `locale` | `string` | `"en-KE"` | BCP 47 locale used to format prices. |
60
+
61
+ ```ts
62
+ type CartProduct = {
63
+ /** Stable identity for the line, used as the React key. Falls back to `name`. */
64
+ id?: string;
65
+ name: string;
66
+ price: number;
67
+ };
68
+ ```
69
+
70
+ Pass an `id` whenever a cart can hold two lines with the same name. Prices are
71
+ formatted with `Intl.NumberFormat`, and the total is summed from `price`.
72
+
73
+ ### `TextInput`
74
+
75
+ ```tsx
76
+ import { TextInput } from "@carbonbits/sixr/text-input";
77
+ ```
78
+
79
+ A labelled text field with optional schema validation. Interactive, so it ships its
80
+ own `"use client"` directive and is reachable only at this subpath — it is not on the
81
+ root barrel, which stays server-safe.
82
+
83
+ | Prop | Type | Default | Description |
84
+ | -------------------- | ------------------------------------ | -------- | ----------------------------------------------------- |
85
+ | `label` | `string` | — | Field label, associated with the input. |
86
+ | `hint` | `ReactNode` | — | Guidance below the field. Replaced by an error. |
87
+ | `error` | `string` | — | Caller-supplied error. Outranks the schema. |
88
+ | `schema` | `TextInputSchema` | — | Any zod schema. Only `safeParse` is called. |
89
+ | `validateOn` | `"blur" \| "change" \| "never"` | `"blur"` | When the schema runs. |
90
+ | `onValidationChange` | `(state: TextInputValidation) => void` | — | Fires on each validation pass. |
91
+ | `onChange` | `(value: string) => void` | — | Receives the value, not the event. |
92
+ | `value` | `string` | — | Makes the field controlled. |
93
+ | `defaultValue` | `string` | `""` | Initial value when uncontrolled. |
94
+ | `optional` | `boolean` | `false` | Shows an "Optional" marker on the label. |
95
+ | `action` | `ReactNode` | — | Trailing label slot, e.g. a "Forgot?" link. |
96
+ | `trailing` | `ReactNode` | — | Trailing in-field slot, e.g. a password peek. |
97
+ | `mono` | `boolean` | `false` | Renders the value in the monospace face. |
98
+ | `type` | `"text" \| "email" \| "password" \| "tel" \| "url" \| "search"` | `"text"` | Input type. |
99
+
100
+ Any other `<input>` attribute — `placeholder`, `maxLength`, `disabled`, `inputMode` —
101
+ is passed straight through.
102
+
103
+ ```tsx
104
+ import { TextInput } from "@carbonbits/sixr/text-input";
105
+ import { emailSchema } from "@carbonbits/sixr/validation";
106
+
107
+ <TextInput
108
+ label="Work email"
109
+ type="email"
110
+ placeholder="you@company.com"
111
+ schema={emailSchema}
112
+ onChange={setEmail}
113
+ />;
114
+ ```
115
+
116
+ Validation runs on blur by default, then on every keystroke once the field has gone
117
+ invalid, so a corrected value clears the message immediately. The first schema issue
118
+ is shown, the input gets `aria-invalid`, and the message is announced via `role="alert"`
119
+ and wired up with `aria-describedby`.
120
+
121
+ `schema` is structurally typed, so any zod schema satisfies it without zod appearing
122
+ in this package's types — you only need zod installed if you use it.
123
+
124
+ ## Validation schemas
125
+
126
+ ```tsx
127
+ import { emailSchema, passwordSchema } from "@carbonbits/sixr/validation";
128
+ ```
129
+
130
+ Ready-made zod schemas matching the Carbonbits auth rules: `emailSchema`,
131
+ `passwordSchema`, `verificationCodeSchema`, `backupCodeSchema`, `fullNameSchema`,
132
+ `jobTitleSchema` and `phoneSchema`. They are ordinary zod schemas — extend them with
133
+ `.refine`, or replace the message with `.min(8, "…")`.
134
+
135
+ This subpath needs `zod` (`^4`), which is an optional peer dependency. Nothing else in
136
+ the package requires it.
137
+
138
+ ## Styling
139
+
140
+ `dist/styles.css` is compiled by Tailwind 4 and contains only the utilities the
141
+ library itself uses, so it is safe to load alongside a consumer's own Tailwind build.
142
+
143
+ Colours and control metrics come from CSS custom properties, all namespaced `--sixr-*`,
144
+ so nothing here can clobber your own Tailwind theme. Override any of them to retheme
145
+ the library without rebuilding it:
146
+
147
+ ```css
148
+ :root {
149
+ --sixr-accent: #0f766e;
150
+ --sixr-line: #d4d4d8;
151
+ }
152
+ ```
153
+
154
+ Dark values follow `prefers-color-scheme` by default. To drive the theme yourself, set
155
+ `data-theme="dark"` or `data-theme="light"` on the root element.
156
+
157
+ ## Versioning
158
+
159
+ Released under [semantic versioning](https://semver.org/): breaking changes only land
160
+ in major versions, so `^` ranges are safe to pin against.
161
+
162
+ ## License
163
+
164
+ MIT
package/dist/cart.cjs ADDED
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+
5
+ // src/components/cart/index.tsx
6
+ function formatPrice(value, currency, locale) {
7
+ return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
8
+ value
9
+ );
10
+ }
11
+ function Cart({
12
+ products = [],
13
+ currency = "KES",
14
+ locale = "en-KE"
15
+ }) {
16
+ const total = products.reduce((sum, product) => sum + product.price, 0);
17
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "cart flex w-full max-w-md flex-col gap-4 rounded-lg border border-gray-200 bg-white p-4", children: [
18
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold text-gray-900", children: "Cart" }),
19
+ products.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "Your cart is empty." }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
20
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-col divide-y divide-gray-100", children: products.map((product) => /* @__PURE__ */ jsxRuntime.jsxs(
21
+ "li",
22
+ {
23
+ className: "flex items-baseline justify-between gap-4 py-2",
24
+ children: [
25
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-800", children: product.name }),
26
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm tabular-nums text-gray-600", children: formatPrice(product.price, currency, locale) })
27
+ ]
28
+ },
29
+ product.id ?? product.name
30
+ )) }),
31
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between gap-4 border-t border-gray-200 pt-2", children: [
32
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-900", children: "Total" }),
33
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold tabular-nums text-gray-900", children: formatPrice(total, currency, locale) })
34
+ ] })
35
+ ] })
36
+ ] });
37
+ }
38
+
39
+ exports.Cart = Cart;
40
+ //# sourceMappingURL=cart.cjs.map
41
+ //# sourceMappingURL=cart.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/cart/index.tsx"],"names":["jsxs","jsx","Fragment"],"mappings":";;;;;AAkBA,SAAS,WAAA,CAAY,KAAA,EAAe,QAAA,EAAkB,MAAA,EAAgB;AACpE,EAAA,OAAO,IAAI,KAAK,YAAA,CAAa,MAAA,EAAQ,EAAE,KAAA,EAAO,UAAA,EAAY,QAAA,EAAU,CAAA,CAAE,MAAA;AAAA,IACpE;AAAA,GACF;AACF;AAEO,SAAS,IAAA,CAAK;AAAA,EACnB,WAAW,EAAC;AAAA,EACZ,QAAA,GAAW,KAAA;AAAA,EACX,MAAA,GAAS;AACX,CAAA,EAAc;AACZ,EAAA,MAAM,KAAA,GAAQ,SAAS,MAAA,CAAO,CAAC,KAAK,OAAA,KAAY,GAAA,GAAM,OAAA,CAAQ,KAAA,EAAO,CAAC,CAAA;AAEtE,EAAA,uBACEA,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yFAAA,EACb,QAAA,EAAA;AAAA,oBAAAC,cAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,qCAAA,EAAsC,QAAA,EAAA,MAAA,EAAI,CAAA;AAAA,IAEvD,QAAA,CAAS,WAAW,CAAA,mBACnBA,cAAA,CAAC,OAAE,SAAA,EAAU,uBAAA,EAAwB,QAAA,EAAA,qBAAA,EAAmB,CAAA,mBAExDD,eAAA,CAAAE,mBAAA,EAAA,EACE,QAAA,EAAA;AAAA,sBAAAD,cAAA,CAAC,QAAG,SAAA,EAAU,wCAAA,EACX,QAAA,EAAA,QAAA,CAAS,GAAA,CAAI,CAAC,OAAA,qBACbD,eAAA;AAAA,QAAC,IAAA;AAAA,QAAA;AAAA,UAEC,SAAA,EAAU,gDAAA;AAAA,UAEV,QAAA,EAAA;AAAA,4BAAAC,cAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,uBAAA,EAAyB,QAAA,EAAA,OAAA,CAAQ,IAAA,EAAK,CAAA;AAAA,4BACtDA,cAAA,CAAC,UAAK,SAAA,EAAU,oCAAA,EACb,sBAAY,OAAA,CAAQ,KAAA,EAAO,QAAA,EAAU,MAAM,CAAA,EAC9C;AAAA;AAAA,SAAA;AAAA,QANK,OAAA,CAAQ,MAAM,OAAA,CAAQ;AAAA,OAQ9B,CAAA,EACH,CAAA;AAAA,sBAEAD,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yEAAA,EACb,QAAA,EAAA;AAAA,wBAAAC,cAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,mCAAA,EAAoC,QAAA,EAAA,OAAA,EAAK,CAAA;AAAA,wBACzDA,cAAA,CAAC,UAAK,SAAA,EAAU,kDAAA,EACb,sBAAY,KAAA,EAAO,QAAA,EAAU,MAAM,CAAA,EACtC;AAAA,OAAA,EACF;AAAA,KAAA,EACF;AAAA,GAAA,EAEJ,CAAA;AAEJ","file":"cart.cjs","sourcesContent":["export type CartProduct = {\n /**\n * Stable identity for this line, used as the React key. Falls back to `name`,\n * so pass an `id` whenever a cart can hold two lines with the same name.\n */\n id?: string;\n name: string;\n price: number;\n};\n\nexport type CartProps = {\n products?: CartProduct[];\n /** ISO 4217 code used to format prices. */\n currency?: string;\n /** BCP 47 locale used to format prices. */\n locale?: string;\n};\n\nfunction formatPrice(value: number, currency: string, locale: string) {\n return new Intl.NumberFormat(locale, { style: \"currency\", currency }).format(\n value,\n );\n}\n\nexport function Cart({\n products = [],\n currency = \"KES\",\n locale = \"en-KE\",\n}: CartProps) {\n const total = products.reduce((sum, product) => sum + product.price, 0);\n\n return (\n <div className=\"cart flex w-full max-w-md flex-col gap-4 rounded-lg border border-gray-200 bg-white p-4\">\n <h1 className=\"text-xl font-semibold text-gray-900\">Cart</h1>\n\n {products.length === 0 ? (\n <p className=\"text-sm text-gray-500\">Your cart is empty.</p>\n ) : (\n <>\n <ul className=\"flex flex-col divide-y divide-gray-100\">\n {products.map((product) => (\n <li\n key={product.id ?? product.name}\n className=\"flex items-baseline justify-between gap-4 py-2\"\n >\n <span className=\"text-sm text-gray-800\">{product.name}</span>\n <span className=\"text-sm tabular-nums text-gray-600\">\n {formatPrice(product.price, currency, locale)}\n </span>\n </li>\n ))}\n </ul>\n\n <div className=\"flex items-baseline justify-between gap-4 border-t border-gray-200 pt-2\">\n <span className=\"text-sm font-medium text-gray-900\">Total</span>\n <span className=\"text-sm font-semibold tabular-nums text-gray-900\">\n {formatPrice(total, currency, locale)}\n </span>\n </div>\n </>\n )}\n </div>\n );\n}\n"]}
@@ -0,0 +1,21 @@
1
+ import * as react from 'react';
2
+
3
+ type CartProduct = {
4
+ /**
5
+ * Stable identity for this line, used as the React key. Falls back to `name`,
6
+ * so pass an `id` whenever a cart can hold two lines with the same name.
7
+ */
8
+ id?: string;
9
+ name: string;
10
+ price: number;
11
+ };
12
+ type CartProps = {
13
+ products?: CartProduct[];
14
+ /** ISO 4217 code used to format prices. */
15
+ currency?: string;
16
+ /** BCP 47 locale used to format prices. */
17
+ locale?: string;
18
+ };
19
+ declare function Cart({ products, currency, locale, }: CartProps): react.JSX.Element;
20
+
21
+ export { Cart, type CartProduct, type CartProps };
package/dist/cart.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ import * as react from 'react';
2
+
3
+ type CartProduct = {
4
+ /**
5
+ * Stable identity for this line, used as the React key. Falls back to `name`,
6
+ * so pass an `id` whenever a cart can hold two lines with the same name.
7
+ */
8
+ id?: string;
9
+ name: string;
10
+ price: number;
11
+ };
12
+ type CartProps = {
13
+ products?: CartProduct[];
14
+ /** ISO 4217 code used to format prices. */
15
+ currency?: string;
16
+ /** BCP 47 locale used to format prices. */
17
+ locale?: string;
18
+ };
19
+ declare function Cart({ products, currency, locale, }: CartProps): react.JSX.Element;
20
+
21
+ export { Cart, type CartProduct, type CartProps };
package/dist/cart.js ADDED
@@ -0,0 +1,3 @@
1
+ export { Cart } from './chunk-BHSOSXTU.js';
2
+ //# sourceMappingURL=cart.js.map
3
+ //# sourceMappingURL=cart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"cart.js"}
@@ -0,0 +1,39 @@
1
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
+
3
+ // src/components/cart/index.tsx
4
+ function formatPrice(value, currency, locale) {
5
+ return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
6
+ value
7
+ );
8
+ }
9
+ function Cart({
10
+ products = [],
11
+ currency = "KES",
12
+ locale = "en-KE"
13
+ }) {
14
+ const total = products.reduce((sum, product) => sum + product.price, 0);
15
+ return /* @__PURE__ */ jsxs("div", { className: "cart flex w-full max-w-md flex-col gap-4 rounded-lg border border-gray-200 bg-white p-4", children: [
16
+ /* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold text-gray-900", children: "Cart" }),
17
+ products.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "Your cart is empty." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
18
+ /* @__PURE__ */ jsx("ul", { className: "flex flex-col divide-y divide-gray-100", children: products.map((product) => /* @__PURE__ */ jsxs(
19
+ "li",
20
+ {
21
+ className: "flex items-baseline justify-between gap-4 py-2",
22
+ children: [
23
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-800", children: product.name }),
24
+ /* @__PURE__ */ jsx("span", { className: "text-sm tabular-nums text-gray-600", children: formatPrice(product.price, currency, locale) })
25
+ ]
26
+ },
27
+ product.id ?? product.name
28
+ )) }),
29
+ /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-4 border-t border-gray-200 pt-2", children: [
30
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-900", children: "Total" }),
31
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tabular-nums text-gray-900", children: formatPrice(total, currency, locale) })
32
+ ] })
33
+ ] })
34
+ ] });
35
+ }
36
+
37
+ export { Cart };
38
+ //# sourceMappingURL=chunk-BHSOSXTU.js.map
39
+ //# sourceMappingURL=chunk-BHSOSXTU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/cart/index.tsx"],"names":[],"mappings":";;;AAkBA,SAAS,WAAA,CAAY,KAAA,EAAe,QAAA,EAAkB,MAAA,EAAgB;AACpE,EAAA,OAAO,IAAI,KAAK,YAAA,CAAa,MAAA,EAAQ,EAAE,KAAA,EAAO,UAAA,EAAY,QAAA,EAAU,CAAA,CAAE,MAAA;AAAA,IACpE;AAAA,GACF;AACF;AAEO,SAAS,IAAA,CAAK;AAAA,EACnB,WAAW,EAAC;AAAA,EACZ,QAAA,GAAW,KAAA;AAAA,EACX,MAAA,GAAS;AACX,CAAA,EAAc;AACZ,EAAA,MAAM,KAAA,GAAQ,SAAS,MAAA,CAAO,CAAC,KAAK,OAAA,KAAY,GAAA,GAAM,OAAA,CAAQ,KAAA,EAAO,CAAC,CAAA;AAEtE,EAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yFAAA,EACb,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,qCAAA,EAAsC,QAAA,EAAA,MAAA,EAAI,CAAA;AAAA,IAEvD,QAAA,CAAS,WAAW,CAAA,mBACnB,GAAA,CAAC,OAAE,SAAA,EAAU,uBAAA,EAAwB,QAAA,EAAA,qBAAA,EAAmB,CAAA,mBAExD,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,QAAG,SAAA,EAAU,wCAAA,EACX,QAAA,EAAA,QAAA,CAAS,GAAA,CAAI,CAAC,OAAA,qBACb,IAAA;AAAA,QAAC,IAAA;AAAA,QAAA;AAAA,UAEC,SAAA,EAAU,gDAAA;AAAA,UAEV,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,uBAAA,EAAyB,QAAA,EAAA,OAAA,CAAQ,IAAA,EAAK,CAAA;AAAA,4BACtD,GAAA,CAAC,UAAK,SAAA,EAAU,oCAAA,EACb,sBAAY,OAAA,CAAQ,KAAA,EAAO,QAAA,EAAU,MAAM,CAAA,EAC9C;AAAA;AAAA,SAAA;AAAA,QANK,OAAA,CAAQ,MAAM,OAAA,CAAQ;AAAA,OAQ9B,CAAA,EACH,CAAA;AAAA,sBAEA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yEAAA,EACb,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,mCAAA,EAAoC,QAAA,EAAA,OAAA,EAAK,CAAA;AAAA,wBACzD,GAAA,CAAC,UAAK,SAAA,EAAU,kDAAA,EACb,sBAAY,KAAA,EAAO,QAAA,EAAU,MAAM,CAAA,EACtC;AAAA,OAAA,EACF;AAAA,KAAA,EACF;AAAA,GAAA,EAEJ,CAAA;AAEJ","file":"chunk-BHSOSXTU.js","sourcesContent":["export type CartProduct = {\n /**\n * Stable identity for this line, used as the React key. Falls back to `name`,\n * so pass an `id` whenever a cart can hold two lines with the same name.\n */\n id?: string;\n name: string;\n price: number;\n};\n\nexport type CartProps = {\n products?: CartProduct[];\n /** ISO 4217 code used to format prices. */\n currency?: string;\n /** BCP 47 locale used to format prices. */\n locale?: string;\n};\n\nfunction formatPrice(value: number, currency: string, locale: string) {\n return new Intl.NumberFormat(locale, { style: \"currency\", currency }).format(\n value,\n );\n}\n\nexport function Cart({\n products = [],\n currency = \"KES\",\n locale = \"en-KE\",\n}: CartProps) {\n const total = products.reduce((sum, product) => sum + product.price, 0);\n\n return (\n <div className=\"cart flex w-full max-w-md flex-col gap-4 rounded-lg border border-gray-200 bg-white p-4\">\n <h1 className=\"text-xl font-semibold text-gray-900\">Cart</h1>\n\n {products.length === 0 ? (\n <p className=\"text-sm text-gray-500\">Your cart is empty.</p>\n ) : (\n <>\n <ul className=\"flex flex-col divide-y divide-gray-100\">\n {products.map((product) => (\n <li\n key={product.id ?? product.name}\n className=\"flex items-baseline justify-between gap-4 py-2\"\n >\n <span className=\"text-sm text-gray-800\">{product.name}</span>\n <span className=\"text-sm tabular-nums text-gray-600\">\n {formatPrice(product.price, currency, locale)}\n </span>\n </li>\n ))}\n </ul>\n\n <div className=\"flex items-baseline justify-between gap-4 border-t border-gray-200 pt-2\">\n <span className=\"text-sm font-medium text-gray-900\">Total</span>\n <span className=\"text-sm font-semibold tabular-nums text-gray-900\">\n {formatPrice(total, currency, locale)}\n </span>\n </div>\n </>\n )}\n </div>\n );\n}\n"]}
package/dist/index.cjs ADDED
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+
5
+ // src/components/cart/index.tsx
6
+ function formatPrice(value, currency, locale) {
7
+ return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
8
+ value
9
+ );
10
+ }
11
+ function Cart({
12
+ products = [],
13
+ currency = "KES",
14
+ locale = "en-KE"
15
+ }) {
16
+ const total = products.reduce((sum, product) => sum + product.price, 0);
17
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "cart flex w-full max-w-md flex-col gap-4 rounded-lg border border-gray-200 bg-white p-4", children: [
18
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold text-gray-900", children: "Cart" }),
19
+ products.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "Your cart is empty." }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
20
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-col divide-y divide-gray-100", children: products.map((product) => /* @__PURE__ */ jsxRuntime.jsxs(
21
+ "li",
22
+ {
23
+ className: "flex items-baseline justify-between gap-4 py-2",
24
+ children: [
25
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-800", children: product.name }),
26
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm tabular-nums text-gray-600", children: formatPrice(product.price, currency, locale) })
27
+ ]
28
+ },
29
+ product.id ?? product.name
30
+ )) }),
31
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between gap-4 border-t border-gray-200 pt-2", children: [
32
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-900", children: "Total" }),
33
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold tabular-nums text-gray-900", children: formatPrice(total, currency, locale) })
34
+ ] })
35
+ ] })
36
+ ] });
37
+ }
38
+
39
+ exports.Cart = Cart;
40
+ //# sourceMappingURL=index.cjs.map
41
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/cart/index.tsx"],"names":["jsxs","jsx","Fragment"],"mappings":";;;;;AAkBA,SAAS,WAAA,CAAY,KAAA,EAAe,QAAA,EAAkB,MAAA,EAAgB;AACpE,EAAA,OAAO,IAAI,KAAK,YAAA,CAAa,MAAA,EAAQ,EAAE,KAAA,EAAO,UAAA,EAAY,QAAA,EAAU,CAAA,CAAE,MAAA;AAAA,IACpE;AAAA,GACF;AACF;AAEO,SAAS,IAAA,CAAK;AAAA,EACnB,WAAW,EAAC;AAAA,EACZ,QAAA,GAAW,KAAA;AAAA,EACX,MAAA,GAAS;AACX,CAAA,EAAc;AACZ,EAAA,MAAM,KAAA,GAAQ,SAAS,MAAA,CAAO,CAAC,KAAK,OAAA,KAAY,GAAA,GAAM,OAAA,CAAQ,KAAA,EAAO,CAAC,CAAA;AAEtE,EAAA,uBACEA,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yFAAA,EACb,QAAA,EAAA;AAAA,oBAAAC,cAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,qCAAA,EAAsC,QAAA,EAAA,MAAA,EAAI,CAAA;AAAA,IAEvD,QAAA,CAAS,WAAW,CAAA,mBACnBA,cAAA,CAAC,OAAE,SAAA,EAAU,uBAAA,EAAwB,QAAA,EAAA,qBAAA,EAAmB,CAAA,mBAExDD,eAAA,CAAAE,mBAAA,EAAA,EACE,QAAA,EAAA;AAAA,sBAAAD,cAAA,CAAC,QAAG,SAAA,EAAU,wCAAA,EACX,QAAA,EAAA,QAAA,CAAS,GAAA,CAAI,CAAC,OAAA,qBACbD,eAAA;AAAA,QAAC,IAAA;AAAA,QAAA;AAAA,UAEC,SAAA,EAAU,gDAAA;AAAA,UAEV,QAAA,EAAA;AAAA,4BAAAC,cAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,uBAAA,EAAyB,QAAA,EAAA,OAAA,CAAQ,IAAA,EAAK,CAAA;AAAA,4BACtDA,cAAA,CAAC,UAAK,SAAA,EAAU,oCAAA,EACb,sBAAY,OAAA,CAAQ,KAAA,EAAO,QAAA,EAAU,MAAM,CAAA,EAC9C;AAAA;AAAA,SAAA;AAAA,QANK,OAAA,CAAQ,MAAM,OAAA,CAAQ;AAAA,OAQ9B,CAAA,EACH,CAAA;AAAA,sBAEAD,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yEAAA,EACb,QAAA,EAAA;AAAA,wBAAAC,cAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,mCAAA,EAAoC,QAAA,EAAA,OAAA,EAAK,CAAA;AAAA,wBACzDA,cAAA,CAAC,UAAK,SAAA,EAAU,kDAAA,EACb,sBAAY,KAAA,EAAO,QAAA,EAAU,MAAM,CAAA,EACtC;AAAA,OAAA,EACF;AAAA,KAAA,EACF;AAAA,GAAA,EAEJ,CAAA;AAEJ","file":"index.cjs","sourcesContent":["export type CartProduct = {\n /**\n * Stable identity for this line, used as the React key. Falls back to `name`,\n * so pass an `id` whenever a cart can hold two lines with the same name.\n */\n id?: string;\n name: string;\n price: number;\n};\n\nexport type CartProps = {\n products?: CartProduct[];\n /** ISO 4217 code used to format prices. */\n currency?: string;\n /** BCP 47 locale used to format prices. */\n locale?: string;\n};\n\nfunction formatPrice(value: number, currency: string, locale: string) {\n return new Intl.NumberFormat(locale, { style: \"currency\", currency }).format(\n value,\n );\n}\n\nexport function Cart({\n products = [],\n currency = \"KES\",\n locale = \"en-KE\",\n}: CartProps) {\n const total = products.reduce((sum, product) => sum + product.price, 0);\n\n return (\n <div className=\"cart flex w-full max-w-md flex-col gap-4 rounded-lg border border-gray-200 bg-white p-4\">\n <h1 className=\"text-xl font-semibold text-gray-900\">Cart</h1>\n\n {products.length === 0 ? (\n <p className=\"text-sm text-gray-500\">Your cart is empty.</p>\n ) : (\n <>\n <ul className=\"flex flex-col divide-y divide-gray-100\">\n {products.map((product) => (\n <li\n key={product.id ?? product.name}\n className=\"flex items-baseline justify-between gap-4 py-2\"\n >\n <span className=\"text-sm text-gray-800\">{product.name}</span>\n <span className=\"text-sm tabular-nums text-gray-600\">\n {formatPrice(product.price, currency, locale)}\n </span>\n </li>\n ))}\n </ul>\n\n <div className=\"flex items-baseline justify-between gap-4 border-t border-gray-200 pt-2\">\n <span className=\"text-sm font-medium text-gray-900\">Total</span>\n <span className=\"text-sm font-semibold tabular-nums text-gray-900\">\n {formatPrice(total, currency, locale)}\n </span>\n </div>\n </>\n )}\n </div>\n );\n}\n"]}
@@ -0,0 +1,64 @@
1
+ export { Cart, CartProduct, CartProps } from './cart.cjs';
2
+ import { InputHTMLAttributes, ReactNode } from 'react';
3
+
4
+ /**
5
+ * Structural shape of a zod schema — every zod schema satisfies it, and any
6
+ * version of zod does. Declaring it this way rather than importing `ZodType`
7
+ * keeps zod out of this module's public types, so consumers who only want an
8
+ * input do not have to install zod to typecheck it.
9
+ */
10
+ type TextInputSchema = {
11
+ safeParse: (value: unknown) => {
12
+ success: true;
13
+ } | {
14
+ success: false;
15
+ error: {
16
+ issues: readonly {
17
+ message: string;
18
+ }[];
19
+ };
20
+ };
21
+ };
22
+ /** Outcome of a single validation pass, handed to `onValidationChange`. */
23
+ type TextInputValidation = {
24
+ valid: boolean;
25
+ /** First issue reported by the schema, or `undefined` when valid. */
26
+ error?: string;
27
+ };
28
+ type TextInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "onChange" | "value" | "defaultValue" | "type"> & {
29
+ label?: string;
30
+ /** Guidance under the field. Replaced by the error message while invalid. */
31
+ hint?: ReactNode;
32
+ /**
33
+ * Error supplied by the caller — a server-side or cross-field failure. Takes
34
+ * precedence over the schema, so a form can hold a field invalid regardless
35
+ * of what `schema` makes of the value on its own.
36
+ */
37
+ error?: string;
38
+ /** Renders the design's muted "Optional" marker at the end of the label. */
39
+ optional?: boolean;
40
+ /** Trailing label slot, for affordances like the design's "Forgot?" link. */
41
+ action?: ReactNode;
42
+ /** Trailing in-field slot, for affordances like the design's password peek. */
43
+ trailing?: ReactNode;
44
+ /** Renders the value in the design's monospace face. */
45
+ mono?: boolean;
46
+ type?: "text" | "email" | "password" | "tel" | "url" | "search";
47
+ value?: string;
48
+ defaultValue?: string;
49
+ onChange?: (value: string) => void;
50
+ /**
51
+ * Any zod schema that accepts a string. Only `safeParse` is used, so a
52
+ * refined, transformed or preprocessed schema works as well as a plain one.
53
+ */
54
+ schema?: TextInputSchema;
55
+ /**
56
+ * When the schema runs. `blur` — the default — avoids scolding someone
57
+ * mid-keystroke; once a field has gone invalid it revalidates on every
58
+ * keystroke so the message clears as soon as it is fixed.
59
+ */
60
+ validateOn?: "blur" | "change" | "never";
61
+ onValidationChange?: (state: TextInputValidation) => void;
62
+ };
63
+
64
+ export type { TextInputProps, TextInputSchema, TextInputValidation };
@@ -0,0 +1,64 @@
1
+ export { Cart, CartProduct, CartProps } from './cart.js';
2
+ import { InputHTMLAttributes, ReactNode } from 'react';
3
+
4
+ /**
5
+ * Structural shape of a zod schema — every zod schema satisfies it, and any
6
+ * version of zod does. Declaring it this way rather than importing `ZodType`
7
+ * keeps zod out of this module's public types, so consumers who only want an
8
+ * input do not have to install zod to typecheck it.
9
+ */
10
+ type TextInputSchema = {
11
+ safeParse: (value: unknown) => {
12
+ success: true;
13
+ } | {
14
+ success: false;
15
+ error: {
16
+ issues: readonly {
17
+ message: string;
18
+ }[];
19
+ };
20
+ };
21
+ };
22
+ /** Outcome of a single validation pass, handed to `onValidationChange`. */
23
+ type TextInputValidation = {
24
+ valid: boolean;
25
+ /** First issue reported by the schema, or `undefined` when valid. */
26
+ error?: string;
27
+ };
28
+ type TextInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "onChange" | "value" | "defaultValue" | "type"> & {
29
+ label?: string;
30
+ /** Guidance under the field. Replaced by the error message while invalid. */
31
+ hint?: ReactNode;
32
+ /**
33
+ * Error supplied by the caller — a server-side or cross-field failure. Takes
34
+ * precedence over the schema, so a form can hold a field invalid regardless
35
+ * of what `schema` makes of the value on its own.
36
+ */
37
+ error?: string;
38
+ /** Renders the design's muted "Optional" marker at the end of the label. */
39
+ optional?: boolean;
40
+ /** Trailing label slot, for affordances like the design's "Forgot?" link. */
41
+ action?: ReactNode;
42
+ /** Trailing in-field slot, for affordances like the design's password peek. */
43
+ trailing?: ReactNode;
44
+ /** Renders the value in the design's monospace face. */
45
+ mono?: boolean;
46
+ type?: "text" | "email" | "password" | "tel" | "url" | "search";
47
+ value?: string;
48
+ defaultValue?: string;
49
+ onChange?: (value: string) => void;
50
+ /**
51
+ * Any zod schema that accepts a string. Only `safeParse` is used, so a
52
+ * refined, transformed or preprocessed schema works as well as a plain one.
53
+ */
54
+ schema?: TextInputSchema;
55
+ /**
56
+ * When the schema runs. `blur` — the default — avoids scolding someone
57
+ * mid-keystroke; once a field has gone invalid it revalidates on every
58
+ * keystroke so the message clears as soon as it is fixed.
59
+ */
60
+ validateOn?: "blur" | "change" | "never";
61
+ onValidationChange?: (state: TextInputValidation) => void;
62
+ };
63
+
64
+ export type { TextInputProps, TextInputSchema, TextInputValidation };
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { Cart } from './chunk-BHSOSXTU.js';
2
+ //# sourceMappingURL=index.js.map
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -0,0 +1,2 @@
1
+ /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-white:#fff;--spacing:.25rem;--container-md:28rem;--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-medium:500;--font-weight-semibold:600;--radius-lg:.5rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.absolute{position:absolute}.relative{position:relative}.top-1\/2{top:50%}.right-\[9px\]{right:9px}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.ml-auto{margin-left:auto}.contents{display:contents}.flex{display:flex}.inline{display:inline}.table{display:table}.h-10{height:calc(var(--spacing) * 10)}.w-full{width:100%}.max-w-md{max-width:var(--container-md)}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.flex-col{flex-direction:column}.items-baseline{align-items:baseline}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-4{gap:calc(var(--spacing) * 4)}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-gray-100>:not(:last-child)){border-color:var(--color-gray-100)}.rounded-field{border-radius:4px}.rounded-lg{border-radius:var(--radius-lg)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-danger{border-color:var(--sixr-danger)}.border-gray-200{border-color:var(--color-gray-200)}.border-line{border-color:var(--sixr-line)}.bg-surface{background-color:var(--sixr-surface)}.bg-white{background-color:var(--color-white)}.p-4{padding:calc(var(--spacing) * 4)}.px-3{padding-inline:calc(var(--spacing) * 3)}.py-2{padding-block:calc(var(--spacing) * 2)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pr-16{padding-right:calc(var(--spacing) * 16)}.font-\[inherit\]{font-family:inherit}.font-code{font-family:var(--sixr-font-code)}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-field{font-size:13px}.text-hint{font-size:11px}.text-label{font-size:11.5px}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.text-danger{color:var(--sixr-danger)}.text-fg{color:var(--sixr-fg)}.text-fg-muted{color:var(--sixr-fg-muted)}.text-fg-subtle{color:var(--sixr-fg-subtle)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-800{color:var(--color-gray-800)}.text-gray-900{color:var(--color-gray-900)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.outline-none{--tw-outline-style:none;outline-style:none}.placeholder\:text-fg-subtle::placeholder{color:var(--sixr-fg-subtle)}.focus\:border-accent:focus{border-color:var(--sixr-accent)}.focus\:border-danger:focus{border-color:var(--sixr-danger)}.focus\:ring-3:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-accent-soft:focus{--tw-ring-color:var(--sixr-accent-soft)}.focus\:ring-danger-soft:focus{--tw-ring-color:var(--sixr-danger-soft)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-45:disabled{opacity:.45}}:root{--sixr-bg:#f4f4f5;--sixr-surface:#fff;--sixr-surface-2:#f4f4f6;--sixr-surface-3:#eaeaec;--sixr-line:#e2e2e5;--sixr-line-strong:#cdcdd2;--sixr-fg:#0b0b0d;--sixr-fg-muted:#55555e;--sixr-fg-subtle:#8e8e99;--sixr-accent:#0b0b0d;--sixr-accent-2:#000;--sixr-accent-soft:#ededef;--sixr-on-accent:#fff;--sixr-success:#16a34a;--sixr-success-soft:#e9f5ec;--sixr-warning:#b45309;--sixr-info:#2563eb;--sixr-danger:#dc2626;--sixr-danger-soft:#fdecec;--sixr-font-code:"JetBrains Mono", ui-monospace, monospace}@media (prefers-color-scheme:dark){:root:not([data-theme=light]){--sixr-bg:#000;--sixr-surface:#0b0b0d;--sixr-surface-2:#121215;--sixr-surface-3:#1b1b1f;--sixr-line:#232328;--sixr-line-strong:#3a3a42;--sixr-fg:#fff;--sixr-fg-muted:#a3a3ad;--sixr-fg-subtle:#70707b;--sixr-accent:#fff;--sixr-accent-2:#e4e4e8;--sixr-accent-soft:#17171b;--sixr-on-accent:#000;--sixr-success:#34d399;--sixr-success-soft:#12301e;--sixr-warning:#fbbf24;--sixr-info:#60a5fa;--sixr-danger:#f87171;--sixr-danger-soft:#2a1416}}:root[data-theme=dark]{--sixr-bg:#000;--sixr-surface:#0b0b0d;--sixr-surface-2:#121215;--sixr-surface-3:#1b1b1f;--sixr-line:#232328;--sixr-line-strong:#3a3a42;--sixr-fg:#fff;--sixr-fg-muted:#a3a3ad;--sixr-fg-subtle:#70707b;--sixr-accent:#fff;--sixr-accent-2:#e4e4e8;--sixr-accent-soft:#17171b;--sixr-on-accent:#000;--sixr-success:#34d399;--sixr-success-soft:#12301e;--sixr-warning:#fbbf24;--sixr-info:#60a5fa;--sixr-danger:#f87171;--sixr-danger-soft:#2a1416}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}