cortena-ui 0.1.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 +59 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +74 -0
- package/dist/index.js.map +1 -0
- package/package.json +74 -0
- package/src/components/button.tsx +75 -0
- package/src/index.ts +4 -0
- package/src/lib/cn.ts +10 -0
- package/src/styles/index.css +25 -0
package/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# cortena-ui
|
|
2
|
+
|
|
3
|
+
The shared React component library for cortenaweb and every Cortena extension.
|
|
4
|
+
shadcn components over [Base UI](https://base-ui.com), styled entirely from
|
|
5
|
+
`cortena-design` tokens. Published to npm as **`cortena-ui`**, versioned in step
|
|
6
|
+
with the tokens, from this repo's `packages/ui`.
|
|
7
|
+
|
|
8
|
+
Intent and decisions: `cortena-docs/cortenaUI&Design/02-ui-components.md`.
|
|
9
|
+
Consuming it: `../../CONSUMING.md`, section "cortena-ui".
|
|
10
|
+
|
|
11
|
+
## Rules for components in this package
|
|
12
|
+
|
|
13
|
+
- **Every visual value is a token.** `pnpm lint:design` runs the same
|
|
14
|
+
`cortena-design-lint` a consumer runs and fails the build on a literal.
|
|
15
|
+
- **Base UI underneath, never Radix.** The rendered element is changed with
|
|
16
|
+
Base UI's `render` prop, not `asChild`. Open state is `data-open`, not
|
|
17
|
+
`data-state`; use the `open:` / `closed:` variants from `styles.css`.
|
|
18
|
+
- **Text utilities carry a type hint.** Tailwind cannot tell whether
|
|
19
|
+
`text-[var(--x)]` is a colour or a size, so write
|
|
20
|
+
`text-[color:var(--ds-foreground)]` and `text-[length:var(--ds-text-caption)]`.
|
|
21
|
+
Backgrounds, borders and radii are unambiguous and need no hint.
|
|
22
|
+
- **Tested in a real browser.** Tests assert computed pixels against the
|
|
23
|
+
token the browser resolved, in light and dark Chromium instances. A dangling
|
|
24
|
+
`var()` paints transparent, which the tests catch and jsdom cannot.
|
|
25
|
+
- **Every component appears in the guide.** `guide/sections.tsx` is the visual
|
|
26
|
+
check in light, dark and system-dark; add an entry with every variant and
|
|
27
|
+
size when adding a component.
|
|
28
|
+
|
|
29
|
+
## Commands
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pnpm check # types
|
|
33
|
+
pnpm lint:design # no literals where a token exists; no dangling var()
|
|
34
|
+
pnpm build # dist/ via tsdown (ESM + d.ts); also runs on prepack
|
|
35
|
+
pnpm test # Vitest browser mode, chromium light + dark
|
|
36
|
+
pnpm guide # three-theme guide on a local Vite server
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
From the repo root the same are `pnpm ui:check`, `ui:build`, `ui:test`, `ui:guide`.
|
|
40
|
+
|
|
41
|
+
## Layout
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
src/index.ts public surface; add an export per component
|
|
45
|
+
src/components/*.tsx one file per component, shadcn shape, Base UI primitives
|
|
46
|
+
src/lib/cn.ts clsx + tailwind-merge
|
|
47
|
+
src/styles/index.css what consumers import after the tokens: animation
|
|
48
|
+
utilities and the Base UI data-attribute variants
|
|
49
|
+
test/ browser tests; setup.css is the consumer recipe verbatim
|
|
50
|
+
guide/ Vite app: ?theme=light|dark|system frames, or all three
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Releasing
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pnpm ui:check && pnpm ui:test
|
|
57
|
+
cd packages/ui && npm version minor && npm publish # prepack builds dist/
|
|
58
|
+
git push --follow-tags
|
|
59
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { ClassValue } from "clsx";
|
|
3
|
+
import { Button as Button$1 } from "@base-ui/react/button";
|
|
4
|
+
import { VariantProps } from "class-variance-authority";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
//#region src/lib/cn.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Merge class names the shadcn way: clsx for conditionals, tailwind-merge so a
|
|
9
|
+
* consumer's `className` wins over a component's default for the same utility.
|
|
10
|
+
*/
|
|
11
|
+
export declare function cn(...inputs: ClassValue[]): string;
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/components/button.d.ts
|
|
14
|
+
/**
|
|
15
|
+
* Button.
|
|
16
|
+
*
|
|
17
|
+
* Base UI supplies the behaviour (native button semantics, `focusableWhenDisabled`,
|
|
18
|
+
* keyboard activation on non-button elements through `render`). Every visual
|
|
19
|
+
* value is a cortena-design token; nothing here is a literal the lint would flag.
|
|
20
|
+
*
|
|
21
|
+
* Radix users: `asChild` is gone. Pass `render={<a href=... />}` to change the
|
|
22
|
+
* rendered element, which is Base UI's equivalent.
|
|
23
|
+
*/
|
|
24
|
+
declare const buttonVariants: (props?: ({
|
|
25
|
+
variant?: "primary" | "secondary" | "outline" | "ghost" | "soft" | "destructive" | "link" | null | undefined;
|
|
26
|
+
size?: "sm" | "md" | "lg" | "icon" | "icon-sm" | null | undefined;
|
|
27
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
28
|
+
interface ButtonProps extends React.ComponentProps<typeof Button$1>, VariantProps<typeof buttonVariants> {}
|
|
29
|
+
declare function Button({ className, variant, size, ...props }: ButtonProps): React.JSX.Element;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { Button, type ButtonProps, buttonVariants };
|
|
32
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { clsx } from "clsx";
|
|
3
|
+
import { twMerge } from "tailwind-merge";
|
|
4
|
+
import { Button as Button$1 } from "@base-ui/react/button";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
//#region src/lib/cn.ts
|
|
8
|
+
/**
|
|
9
|
+
* Merge class names the shadcn way: clsx for conditionals, tailwind-merge so a
|
|
10
|
+
* consumer's `className` wins over a component's default for the same utility.
|
|
11
|
+
*/
|
|
12
|
+
function cn(...inputs) {
|
|
13
|
+
return twMerge(clsx(inputs));
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/components/button.tsx
|
|
17
|
+
/**
|
|
18
|
+
* Button.
|
|
19
|
+
*
|
|
20
|
+
* Base UI supplies the behaviour (native button semantics, `focusableWhenDisabled`,
|
|
21
|
+
* keyboard activation on non-button elements through `render`). Every visual
|
|
22
|
+
* value is a cortena-design token; nothing here is a literal the lint would flag.
|
|
23
|
+
*
|
|
24
|
+
* Radix users: `asChild` is gone. Pass `render={<a href=... />}` to change the
|
|
25
|
+
* rendered element, which is Base UI's equivalent.
|
|
26
|
+
*/
|
|
27
|
+
const buttonVariants = cva([
|
|
28
|
+
"inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap",
|
|
29
|
+
"font-medium",
|
|
30
|
+
"rounded-[var(--ds-radius-md)]",
|
|
31
|
+
"border",
|
|
32
|
+
"transition-[background-color,border-color,color,box-shadow,filter]",
|
|
33
|
+
"duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]",
|
|
34
|
+
"outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40",
|
|
35
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
36
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0"
|
|
37
|
+
], {
|
|
38
|
+
variants: {
|
|
39
|
+
variant: {
|
|
40
|
+
primary: "border-[var(--ds-primary)] bg-[var(--ds-primary)] text-[color:var(--ds-primary-foreground)] hover:border-[var(--ds-primary-hover)] hover:bg-[var(--ds-primary-hover)]",
|
|
41
|
+
secondary: "border-[var(--ds-border)] bg-[var(--ds-secondary)] text-[color:var(--ds-secondary-foreground)] hover:border-[var(--ds-border-strong)] hover:bg-[var(--ds-hover)]",
|
|
42
|
+
outline: "border-[var(--ds-border-strong)] bg-transparent text-[color:var(--ds-foreground)] hover:bg-[var(--ds-hover)]",
|
|
43
|
+
ghost: "border-transparent bg-transparent text-[color:var(--ds-muted-foreground)] hover:bg-[var(--ds-hover)] hover:text-[color:var(--ds-foreground)]",
|
|
44
|
+
soft: "border-transparent bg-[var(--ds-primary-soft)] text-[color:var(--ds-primary-soft-foreground)] hover:bg-[var(--ds-primary-soft)] hover:brightness-95",
|
|
45
|
+
destructive: "border-[var(--ds-destructive)] bg-[var(--ds-destructive)] text-[color:var(--ds-destructive-foreground)] hover:brightness-110",
|
|
46
|
+
link: "border-transparent bg-transparent text-[color:var(--ds-primary)] underline-offset-4 hover:underline"
|
|
47
|
+
},
|
|
48
|
+
size: {
|
|
49
|
+
sm: "h-8 px-3 text-[length:var(--ds-text-caption)] rounded-[var(--ds-radius-sm)] [&_svg]:size-3.5",
|
|
50
|
+
md: "h-9 px-4 text-[length:var(--ds-text-caption-lg)] [&_svg]:size-4",
|
|
51
|
+
lg: "h-10 px-5 text-[length:var(--ds-text-body-sm)] [&_svg]:size-4",
|
|
52
|
+
icon: "size-9 [&_svg]:size-4",
|
|
53
|
+
"icon-sm": "size-8 rounded-[var(--ds-radius-sm)] [&_svg]:size-3.5"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
defaultVariants: {
|
|
57
|
+
variant: "primary",
|
|
58
|
+
size: "md"
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
function Button({ className, variant, size, ...props }) {
|
|
62
|
+
return /* @__PURE__ */ jsx(Button$1, {
|
|
63
|
+
"data-slot": "button",
|
|
64
|
+
className: cn(buttonVariants({
|
|
65
|
+
variant,
|
|
66
|
+
size
|
|
67
|
+
}), className),
|
|
68
|
+
...props
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
export { Button, buttonVariants, cn };
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["BaseButton"],"sources":["../src/lib/cn.ts","../src/components/button.tsx"],"sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names the shadcn way: clsx for conditionals, tailwind-merge so a\n * consumer's `className` wins over a component's default for the same utility.\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport { Button as BaseButton } from \"@base-ui/react/button\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type * as React from \"react\";\nimport { cn } from \"@/lib/cn\";\n\n/**\n * Button.\n *\n * Base UI supplies the behaviour (native button semantics, `focusableWhenDisabled`,\n * keyboard activation on non-button elements through `render`). Every visual\n * value is a cortena-design token; nothing here is a literal the lint would flag.\n *\n * Radix users: `asChild` is gone. Pass `render={<a href=... />}` to change the\n * rendered element, which is Base UI's equivalent.\n */\nconst buttonVariants = cva(\n [\n \"inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap\",\n \"font-medium\",\n \"rounded-[var(--ds-radius-md)]\",\n \"border\",\n \"transition-[background-color,border-color,color,box-shadow,filter]\",\n \"duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]\",\n \"outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40\",\n \"disabled:pointer-events-none disabled:opacity-50\",\n \"[&_svg]:pointer-events-none [&_svg]:shrink-0\",\n ],\n {\n variants: {\n variant: {\n primary:\n \"border-[var(--ds-primary)] bg-[var(--ds-primary)] text-[color:var(--ds-primary-foreground)] hover:border-[var(--ds-primary-hover)] hover:bg-[var(--ds-primary-hover)]\",\n secondary:\n \"border-[var(--ds-border)] bg-[var(--ds-secondary)] text-[color:var(--ds-secondary-foreground)] hover:border-[var(--ds-border-strong)] hover:bg-[var(--ds-hover)]\",\n outline:\n \"border-[var(--ds-border-strong)] bg-transparent text-[color:var(--ds-foreground)] hover:bg-[var(--ds-hover)]\",\n ghost:\n \"border-transparent bg-transparent text-[color:var(--ds-muted-foreground)] hover:bg-[var(--ds-hover)] hover:text-[color:var(--ds-foreground)]\",\n soft:\n \"border-transparent bg-[var(--ds-primary-soft)] text-[color:var(--ds-primary-soft-foreground)] hover:bg-[var(--ds-primary-soft)] hover:brightness-95\",\n destructive:\n \"border-[var(--ds-destructive)] bg-[var(--ds-destructive)] text-[color:var(--ds-destructive-foreground)] hover:brightness-110\",\n link: \"border-transparent bg-transparent text-[color:var(--ds-primary)] underline-offset-4 hover:underline\",\n },\n size: {\n sm: \"h-8 px-3 text-[length:var(--ds-text-caption)] rounded-[var(--ds-radius-sm)] [&_svg]:size-3.5\",\n md: \"h-9 px-4 text-[length:var(--ds-text-caption-lg)] [&_svg]:size-4\",\n lg: \"h-10 px-5 text-[length:var(--ds-text-body-sm)] [&_svg]:size-4\",\n icon: \"size-9 [&_svg]:size-4\",\n \"icon-sm\": \"size-8 rounded-[var(--ds-radius-sm)] [&_svg]:size-3.5\",\n },\n },\n defaultVariants: {\n variant: \"primary\",\n size: \"md\",\n },\n },\n);\n\nexport interface ButtonProps\n extends React.ComponentProps<typeof BaseButton>, VariantProps<typeof buttonVariants> {}\n\nfunction Button({ className, variant, size, ...props }: ButtonProps) {\n return (\n <BaseButton\n data-slot=\"button\"\n className={cn(buttonVariants({ variant, size }), className)}\n {...props}\n />\n );\n}\n\nexport { Button, buttonVariants };\n"],"mappings":";;;;;;;;;;;AAOA,SAAgB,GAAG,GAAG,QAA8B;CAClD,OAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B;;;;;;;;;;;;;ACQA,MAAM,iBAAiB,IACrB;CACE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,GACA;CACE,UAAU;EACR,SAAS;GACP,SACE;GACF,WACE;GACF,SACE;GACF,OACE;GACF,MACE;GACF,aACE;GACF,MAAM;EACR;EACA,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,MAAM;GACN,WAAW;EACb;CACF;CACA,iBAAiB;EACf,SAAS;EACT,MAAM;CACR;AACF,CACF;AAKA,SAAS,OAAO,EAAE,WAAW,SAAS,MAAM,GAAG,SAAsB;CACnE,OACE,oBAACA,UAAD;EACE,aAAU;EACV,WAAW,GAAG,eAAe;GAAE;GAAS;EAAK,CAAC,GAAG,SAAS;EAC1D,GAAI;CACL,CAAA;AAEL"}
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cortena-ui",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Cortena UI — the shared React component library for cortenaweb and every Cortena extension. shadcn over Base UI, styled entirely from cortena-design tokens.",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/AscendendenceAI/cortena-design.git",
|
|
9
|
+
"directory": "packages/ui"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"sideEffects": [
|
|
13
|
+
"*.css"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=22"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"src",
|
|
21
|
+
"README.md"
|
|
22
|
+
],
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"default": "./dist/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./styles.css": "./src/styles/index.css",
|
|
29
|
+
"./package.json": "./package.json"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsdown",
|
|
33
|
+
"check": "tsc --noEmit",
|
|
34
|
+
"lint:design": "node ../../lint/cli.mjs src",
|
|
35
|
+
"test": "vitest run",
|
|
36
|
+
"guide": "vite --config guide/vite.config.ts",
|
|
37
|
+
"guide:build": "vite build --config guide/vite.config.ts",
|
|
38
|
+
"prepack": "pnpm build"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"cortena-design": ">=3.1.0",
|
|
42
|
+
"react": "^19.0.0",
|
|
43
|
+
"react-dom": "^19.0.0"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@base-ui/react": "^1.8.0",
|
|
47
|
+
"class-variance-authority": "^0.7.1",
|
|
48
|
+
"clsx": "^2.1.1",
|
|
49
|
+
"lucide-react": "^1.41.0",
|
|
50
|
+
"tailwind-merge": "^3.6.0",
|
|
51
|
+
"tw-animate-css": "^1.4.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@tailwindcss/vite": "^4.3.3",
|
|
55
|
+
"@types/node": "^22.20.1",
|
|
56
|
+
"@types/react": "^19.1.0",
|
|
57
|
+
"@types/react-dom": "^19.1.0",
|
|
58
|
+
"@vitejs/plugin-react": "^6.1.1",
|
|
59
|
+
"@vitest/browser-playwright": "^5.0.0",
|
|
60
|
+
"cortena-design": "workspace:*",
|
|
61
|
+
"playwright": "^1.63.0",
|
|
62
|
+
"react": "^19.1.0",
|
|
63
|
+
"react-dom": "^19.1.0",
|
|
64
|
+
"tailwindcss": "^4.3.3",
|
|
65
|
+
"tsdown": "^0.23.0",
|
|
66
|
+
"typescript": "^5.9.0",
|
|
67
|
+
"vite": "^8.2.2",
|
|
68
|
+
"vitest": "^5.0.0",
|
|
69
|
+
"vitest-browser-react": "^2.3.0"
|
|
70
|
+
},
|
|
71
|
+
"publishConfig": {
|
|
72
|
+
"access": "public"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Button as BaseButton } from "@base-ui/react/button";
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
+
import type * as React from "react";
|
|
6
|
+
import { cn } from "@/lib/cn";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Button.
|
|
10
|
+
*
|
|
11
|
+
* Base UI supplies the behaviour (native button semantics, `focusableWhenDisabled`,
|
|
12
|
+
* keyboard activation on non-button elements through `render`). Every visual
|
|
13
|
+
* value is a cortena-design token; nothing here is a literal the lint would flag.
|
|
14
|
+
*
|
|
15
|
+
* Radix users: `asChild` is gone. Pass `render={<a href=... />}` to change the
|
|
16
|
+
* rendered element, which is Base UI's equivalent.
|
|
17
|
+
*/
|
|
18
|
+
const buttonVariants = cva(
|
|
19
|
+
[
|
|
20
|
+
"inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap",
|
|
21
|
+
"font-medium",
|
|
22
|
+
"rounded-[var(--ds-radius-md)]",
|
|
23
|
+
"border",
|
|
24
|
+
"transition-[background-color,border-color,color,box-shadow,filter]",
|
|
25
|
+
"duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]",
|
|
26
|
+
"outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40",
|
|
27
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
28
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
29
|
+
],
|
|
30
|
+
{
|
|
31
|
+
variants: {
|
|
32
|
+
variant: {
|
|
33
|
+
primary:
|
|
34
|
+
"border-[var(--ds-primary)] bg-[var(--ds-primary)] text-[color:var(--ds-primary-foreground)] hover:border-[var(--ds-primary-hover)] hover:bg-[var(--ds-primary-hover)]",
|
|
35
|
+
secondary:
|
|
36
|
+
"border-[var(--ds-border)] bg-[var(--ds-secondary)] text-[color:var(--ds-secondary-foreground)] hover:border-[var(--ds-border-strong)] hover:bg-[var(--ds-hover)]",
|
|
37
|
+
outline:
|
|
38
|
+
"border-[var(--ds-border-strong)] bg-transparent text-[color:var(--ds-foreground)] hover:bg-[var(--ds-hover)]",
|
|
39
|
+
ghost:
|
|
40
|
+
"border-transparent bg-transparent text-[color:var(--ds-muted-foreground)] hover:bg-[var(--ds-hover)] hover:text-[color:var(--ds-foreground)]",
|
|
41
|
+
soft:
|
|
42
|
+
"border-transparent bg-[var(--ds-primary-soft)] text-[color:var(--ds-primary-soft-foreground)] hover:bg-[var(--ds-primary-soft)] hover:brightness-95",
|
|
43
|
+
destructive:
|
|
44
|
+
"border-[var(--ds-destructive)] bg-[var(--ds-destructive)] text-[color:var(--ds-destructive-foreground)] hover:brightness-110",
|
|
45
|
+
link: "border-transparent bg-transparent text-[color:var(--ds-primary)] underline-offset-4 hover:underline",
|
|
46
|
+
},
|
|
47
|
+
size: {
|
|
48
|
+
sm: "h-8 px-3 text-[length:var(--ds-text-caption)] rounded-[var(--ds-radius-sm)] [&_svg]:size-3.5",
|
|
49
|
+
md: "h-9 px-4 text-[length:var(--ds-text-caption-lg)] [&_svg]:size-4",
|
|
50
|
+
lg: "h-10 px-5 text-[length:var(--ds-text-body-sm)] [&_svg]:size-4",
|
|
51
|
+
icon: "size-9 [&_svg]:size-4",
|
|
52
|
+
"icon-sm": "size-8 rounded-[var(--ds-radius-sm)] [&_svg]:size-3.5",
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
defaultVariants: {
|
|
56
|
+
variant: "primary",
|
|
57
|
+
size: "md",
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
export interface ButtonProps
|
|
63
|
+
extends React.ComponentProps<typeof BaseButton>, VariantProps<typeof buttonVariants> {}
|
|
64
|
+
|
|
65
|
+
function Button({ className, variant, size, ...props }: ButtonProps) {
|
|
66
|
+
return (
|
|
67
|
+
<BaseButton
|
|
68
|
+
data-slot="button"
|
|
69
|
+
className={cn(buttonVariants({ variant, size }), className)}
|
|
70
|
+
{...props}
|
|
71
|
+
/>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export { Button, buttonVariants };
|
package/src/index.ts
ADDED
package/src/lib/cn.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { clsx, type ClassValue } from "clsx";
|
|
2
|
+
import { twMerge } from "tailwind-merge";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Merge class names the shadcn way: clsx for conditionals, tailwind-merge so a
|
|
6
|
+
* consumer's `className` wins over a component's default for the same utility.
|
|
7
|
+
*/
|
|
8
|
+
export function cn(...inputs: ClassValue[]): string {
|
|
9
|
+
return twMerge(clsx(inputs));
|
|
10
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* cortena-ui/styles.css
|
|
3
|
+
*
|
|
4
|
+
* Import this AFTER the token layer, in the consumer's Tailwind entry file:
|
|
5
|
+
*
|
|
6
|
+
* @import "tailwindcss";
|
|
7
|
+
* @import "cortena-design/fonts.css";
|
|
8
|
+
* @import "cortena-design/tokens.css";
|
|
9
|
+
* @import "cortena-ui/styles.css";
|
|
10
|
+
* @source "../node_modules/cortena-ui/src";
|
|
11
|
+
*
|
|
12
|
+
* It carries only what components need beyond the tokens: the enter/exit
|
|
13
|
+
* animation utilities shadcn components rely on, and a custom variant for
|
|
14
|
+
* Base UI's data attributes. Every colour, size, radius, shadow and duration
|
|
15
|
+
* comes from cortena-design; nothing here restates a token. The `@source`
|
|
16
|
+
* line is what makes the consumer's Tailwind build generate the utility
|
|
17
|
+
* classes used inside this package's components.
|
|
18
|
+
*/
|
|
19
|
+
@import "tw-animate-css";
|
|
20
|
+
|
|
21
|
+
/* Base UI marks open/closed state with data-open / data-closed rather than
|
|
22
|
+
Radix's data-state, so give components a variant for each. */
|
|
23
|
+
@custom-variant open (&[data-open]);
|
|
24
|
+
@custom-variant closed (&[data-closed]);
|
|
25
|
+
@custom-variant popup-open (&[data-popup-open]);
|