@nautui/core 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.
Files changed (54) hide show
  1. package/README.md +148 -0
  2. package/package.json +35 -0
  3. package/src/components/Accordion.astro +24 -0
  4. package/src/components/AccordionItem.astro +172 -0
  5. package/src/components/Background.astro +75 -0
  6. package/src/components/Badge.astro +140 -0
  7. package/src/components/Bento.astro +37 -0
  8. package/src/components/BentoItem.astro +26 -0
  9. package/src/components/Box.astro +189 -0
  10. package/src/components/Breadcrumb.astro +62 -0
  11. package/src/components/Button.astro +273 -0
  12. package/src/components/Card.astro +228 -0
  13. package/src/components/Container.astro +76 -0
  14. package/src/components/Divider.astro +85 -0
  15. package/src/components/Drawer.astro +139 -0
  16. package/src/components/Flex.astro +150 -0
  17. package/src/components/Flow.astro +119 -0
  18. package/src/components/Grid.astro +335 -0
  19. package/src/components/GridItem.astro +225 -0
  20. package/src/components/Group.astro +106 -0
  21. package/src/components/Image.astro +191 -0
  22. package/src/components/Link.astro +118 -0
  23. package/src/components/List.astro +57 -0
  24. package/src/components/ListItem.astro +31 -0
  25. package/src/components/Mark.astro +161 -0
  26. package/src/components/Marquee.astro +193 -0
  27. package/src/components/Masonry.astro +75 -0
  28. package/src/components/MasonryItem.astro +28 -0
  29. package/src/components/Menu.astro +71 -0
  30. package/src/components/MenuItem.astro +93 -0
  31. package/src/components/NavBar.astro +211 -0
  32. package/src/components/Section.astro +108 -0
  33. package/src/components/Space.astro +400 -0
  34. package/src/components/Stack.astro +237 -0
  35. package/src/components/Text.astro +270 -0
  36. package/src/components/Theme.astro +37 -0
  37. package/src/components/ThemeToggle.astro +141 -0
  38. package/src/components/Title.astro +141 -0
  39. package/src/index.d.ts +80 -0
  40. package/src/index.ts +77 -0
  41. package/src/lib/border.ts +92 -0
  42. package/src/lib/pattern.ts +37 -0
  43. package/src/lib/spacing.ts +48 -0
  44. package/src/styles/border.css +180 -0
  45. package/src/styles/colors.css +99 -0
  46. package/src/styles/global.css +57 -0
  47. package/src/styles/radius.css +22 -0
  48. package/src/styles/shadow.css +11 -0
  49. package/src/styles/spacing/display.css +198 -0
  50. package/src/styles/spacing/gap.css +19 -0
  51. package/src/styles/spacing/margin.css +157 -0
  52. package/src/styles/spacing/padding.css +154 -0
  53. package/src/styles/spacing/spacing.css +2 -0
  54. package/src/types.ts +10 -0
package/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # Core Components for Naut UI
2
+
3
+ Low-level, reusable building blocks for marketing websites built with Astro.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @nautui/core
9
+ # or
10
+ pnpm add @nautui/core
11
+ # or
12
+ bun add @nautui/core
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ### 1. Add Theme Provider
18
+
19
+ Wrap your layout with the theme provider to enable theming:
20
+
21
+ ```astro
22
+ ---
23
+ import { Theme } from "@nautui/core";
24
+ ---
25
+
26
+ <html lang="en">
27
+ <body>
28
+ <Theme>
29
+ <slot />
30
+ </Theme>
31
+ </body>
32
+ </html>
33
+ ```
34
+
35
+ ### 2. Use Components
36
+
37
+ ```astro
38
+ ---
39
+ import { Button, Container, Section, Title, Text } from "@nautui/core";
40
+ ---
41
+
42
+ <Section variant="dimmed">
43
+ <Container>
44
+ <Title level={1}>Welcome</Title>
45
+ <Text>Get started with Astro NautUI</Text>
46
+ <Button variant="primary">Get Started</Button>
47
+ </Container>
48
+ </Section>
49
+ ```
50
+
51
+ ## Components
52
+
53
+ ### Theme
54
+ - [x] `Theme` — provider that injects tokens and wires up auto dark mode
55
+ - [x] `ThemeToggle` — button that switches between light and dark, persists choice
56
+
57
+ ### Layouts
58
+ - [x] `Container` — center content with padding and max-width
59
+ - [x] `Box` — low-level layout component for spacing, borders, backgrounds
60
+ - [x] `Section` — full-width page section with variants (dimmed, highlight, inverted)
61
+ - [x] `Group` — flex container helper with gap and alignment shortcuts
62
+ - [x] `Grid` — responsive 1–6 column grid with configurable gap
63
+
64
+ ### Elements
65
+ - [x] `Button` — link or button with 11 variants (primary, secondary, outline, ghost, destructive, rainbow, …)
66
+ - [x] `Card` — surface container with default, bordered, and flat variants
67
+ - [x] `Divider` — horizontal rule styled with theme tokens
68
+ - [x] `Badge` — small pill label for status, counts, or tags
69
+ - [x] `Image` — responsive image with optional caption
70
+
71
+ ### Typography
72
+ - [x] `Title` — semantic h1–h6 with consistent sizing
73
+ - [x] `Text` — body text with size variants
74
+ - [x] `Mark` — `<mark>` styled with highlight color
75
+ - [x] `Link` — themed anchor with hover and focus states
76
+ - [x] `List` — styled ordered and unordered lists
77
+ - [ ] `Quote` — blockquote with themed border
78
+
79
+ ### Navigation
80
+ - [x] `Navbar` — horizontal site navigation
81
+ - [x] `Drawer` — off-canvas sidebar for mobile
82
+ - [x] `Breadcrumbs` — hierarchical page links
83
+
84
+ ## Dark Mode
85
+
86
+ Dark mode is enabled by default. Disable it with `dark={false}`:
87
+
88
+ ```astro
89
+ <Theme dark={false}><slot /></Theme>
90
+ ```
91
+
92
+ Use the toggle for manual switching:
93
+
94
+ ```astro
95
+ <ThemeToggle />
96
+ ```
97
+
98
+ ## Theming
99
+
100
+ Provide two brand colors, and the palette is derived at runtime using CSS `color-mix()` and OKLCH.
101
+
102
+ ### Required Tokens
103
+
104
+ ```astro
105
+ <style>
106
+ :root {
107
+ --naut-color-primary: #ffb000; /* must meet ≥ 4.5:1 vs white */
108
+ --naut-color-secondary: #555522; /* must meet ≥ 4.5:1 vs white */
109
+ }
110
+ </style>
111
+ ```
112
+
113
+ ### Optional Overrides
114
+
115
+ ```astro
116
+ <style>
117
+ :root {
118
+ /* Brand colors */
119
+ --naut-color-primary: #ffb000;
120
+ --naut-color-secondary: #555522;
121
+ --naut-color-destructive: #ff2222;
122
+
123
+ /* Tint strengths */
124
+ --naut-tint: 3%;
125
+ --naut-tint-base: 3%;
126
+ --naut-tint-primary: 25%;
127
+ --naut-tint-secondary: 35%;
128
+ --naut-tint-destructive: 10%;
129
+
130
+ /* Typography */
131
+ --naut-font-family: "Inter", sans-serif;
132
+ }
133
+ </style>
134
+ ```
135
+
136
+ All other tokens (`--naut-color-base`, `--naut-color-text`, `--naut-color-surface`, etc.) are derived automatically.
137
+
138
+ ### Browser Support
139
+
140
+ Baseline 2024: Chrome 119+, Safari 16.4+, Firefox 128+.
141
+
142
+ ## Icons
143
+
144
+ Recommended: [Lucide Icons](https://lucide.dev/guide/astro/).
145
+
146
+ ## License
147
+
148
+ MIT
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@nautui/core",
3
+ "description": "Core components for NautUI",
4
+ "version": "0.1.0",
5
+ "author": "Virak Hor",
6
+ "homepage": "https://github.com/viirak/nautui",
7
+ "repository": "https://github.com/viirak/nautui",
8
+ "bugs": "https://github.com/viirak/nautui/issues",
9
+ "license": "MIT",
10
+ "keywords": [
11
+ "Astro",
12
+ "UI",
13
+ "Components",
14
+ "library",
15
+ "NautUI"
16
+ ],
17
+ "main": "./src/index.ts",
18
+ "types": "./src/index.d.ts",
19
+ "files": [
20
+ "src"
21
+ ],
22
+ "exports": {
23
+ ".": "./src/index.ts"
24
+ },
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "peerDependencies": {
29
+ "astro": "^4.0.0 || ^5.0.0"
30
+ },
31
+ "scripts": {
32
+ "lint": "biome check src",
33
+ "test": "echo \"Error: no test specified\" && exit 1"
34
+ }
35
+ }
@@ -0,0 +1,24 @@
1
+ ---
2
+ import type { Base } from "../types";
3
+
4
+ export interface AccordionProps extends Base {}
5
+
6
+ const { class: className, ...rest } = Astro.props as AccordionProps;
7
+ ---
8
+
9
+ <div
10
+ class:list={[
11
+ "naut-accordion",
12
+ className
13
+ ]}
14
+ {...rest}
15
+ >
16
+ <slot />
17
+ </div>
18
+
19
+ <style>
20
+ .naut-accordion {
21
+ display: flex;
22
+ flex-direction: column;
23
+ }
24
+ </style>
@@ -0,0 +1,172 @@
1
+ ---
2
+ import type { Base } from "../types";
3
+ import Text from "./Text.astro";
4
+
5
+ export interface AccordionItemProps extends Base {
6
+ icon?: "chevron" | "plus";
7
+ size?: "md" | "lg" | "xl";
8
+ title: string;
9
+ }
10
+
11
+ const {
12
+ title,
13
+ size = "md",
14
+ icon = "plus",
15
+ class: className,
16
+ ...rest
17
+ } = Astro.props as AccordionItemProps;
18
+
19
+ const uniqueId = Math.random().toString(36).slice(2, 9);
20
+ ---
21
+
22
+ <div
23
+ class:list={[
24
+ "naut-accordion-item",
25
+ `size-${size}`,
26
+ className
27
+ ]}
28
+ {...rest}
29
+ >
30
+ <input
31
+ type="radio"
32
+ id={uniqueId}
33
+ name="accordion-group"
34
+ class="accordion-radio"
35
+ hidden
36
+ >
37
+ <label class="accordion-header" for={uniqueId}>
38
+ <Text inline size={size}>{title}</Text>
39
+ <span class:list={["accordion-icon", icon]}></span>
40
+ </label>
41
+ <div class="accordion-content">
42
+ <div class="content-inner"><slot /></div>
43
+ </div>
44
+ </div>
45
+
46
+ <style>
47
+ .naut-accordion-item {
48
+ display: block;
49
+
50
+ &::after {
51
+ display: block;
52
+ margin: 0 var(--naut-spacing-sm);
53
+ content: "";
54
+ border-bottom: 1px solid var(--naut-color-border);
55
+ }
56
+
57
+ &:last-child::after {
58
+ display: none;
59
+ }
60
+
61
+ .accordion-header {
62
+ display: flex;
63
+ align-items: center;
64
+ justify-content: space-between;
65
+ padding: var(--naut-spacing-sm);
66
+ cursor: pointer;
67
+ user-select: none;
68
+ background-color: var(--accordion-item-bg);
69
+
70
+ .accordion-icon {
71
+ position: relative;
72
+ display: block;
73
+ margin: 8px;
74
+ opacity: 0.5;
75
+ transition:
76
+ transform 0.3s ease-in-out,
77
+ opacity 0.3s ease-in-out;
78
+
79
+ &.chevron {
80
+ width: 8px;
81
+ height: 8px;
82
+ border-right: var(--naut-border-width-md) solid
83
+ var(--naut-color-content);
84
+ border-bottom: var(--naut-border-width-md) solid
85
+ var(--naut-color-content);
86
+ transform: rotate(45deg); /* Points down */
87
+ }
88
+
89
+ &.plus {
90
+ width: 16px;
91
+ height: 16px;
92
+
93
+ /* Horizontal Base Line (-) */
94
+ &::before {
95
+ position: absolute;
96
+ top: 50%;
97
+ left: 0;
98
+ width: 100%;
99
+ height: var(--naut-border-width-md);
100
+ content: "";
101
+ background-color: var(--naut-color-content);
102
+ transform: translateY(-50%);
103
+ transition: transform 0.3s ease-in-out;
104
+ }
105
+
106
+ /* Vertical Crossing Line (|) */
107
+ &::after {
108
+ position: absolute;
109
+ top: 0;
110
+ left: 50%;
111
+ width: var(--naut-border-width-md);
112
+ height: 100%;
113
+ content: "";
114
+ background-color: var(--naut-color-content);
115
+ transform: translateX(-50%);
116
+ transition:
117
+ transform 0.3s ease-in-out,
118
+ opacity 0.3s ease-in-out;
119
+ }
120
+ }
121
+ }
122
+ }
123
+
124
+ .accordion-content {
125
+ max-height: 0;
126
+ overflow: hidden;
127
+ transition: max-height 0.3s ease-in-out;
128
+
129
+ .content-inner {
130
+ padding: var(--naut-spacing-sm);
131
+ padding-top: 0;
132
+ }
133
+ }
134
+
135
+ .accordion-radio:checked {
136
+ ~ .accordion-header {
137
+ .accordion-icon.chevron {
138
+ opacity: 1;
139
+ transform: rotate(-135deg);
140
+ }
141
+
142
+ .accordion-icon.plus {
143
+ opacity: 1;
144
+
145
+ &::after {
146
+ opacity: 0;
147
+ transform: translateX(-50%) scaleY(0);
148
+ }
149
+ &::before {
150
+ transform: translateY(-50%) rotate(180deg); /* Smooth spin */
151
+ }
152
+ }
153
+ }
154
+ ~ .accordion-content {
155
+ max-height: 400px;
156
+ }
157
+ }
158
+ }
159
+ </style>
160
+
161
+ <script>
162
+ const headers = document.querySelectorAll(".accordion-header");
163
+ for (const header of headers) {
164
+ header.addEventListener("click", (event) => {
165
+ const radio = header.previousElementSibling as HTMLInputElement;
166
+ if (radio?.checked) {
167
+ event.preventDefault(); // Stop default browser lock
168
+ radio.checked = false; // Manually collapse the item
169
+ }
170
+ });
171
+ }
172
+ </script>
@@ -0,0 +1,75 @@
1
+ ---
2
+ import { getGradientPattern, type PatternProps } from "../lib/pattern";
3
+
4
+ interface GradientProps {
5
+ colors: string[];
6
+ deg?: number;
7
+ type: "linear" | "radial";
8
+ }
9
+
10
+ export interface BackgroundProps {
11
+ color?: string;
12
+ gradient?: GradientProps;
13
+ image?: string;
14
+ opacity?: number;
15
+ pattern?: PatternProps;
16
+ }
17
+
18
+ const { color, image, pattern, gradient, opacity } =
19
+ Astro.props as BackgroundProps;
20
+ const backgroundColor = color || "transparent";
21
+ const backgroundImage = image ? `url("${image}")` : undefined;
22
+ const backgroundSize = image ? "cover" : undefined;
23
+ const backgroundPosition = image ? "center" : undefined;
24
+ const backgroundRepeat = image ? "no-repeat" : undefined;
25
+ const backgroundOpacity = opacity || 1;
26
+ const backgroundPattern = getGradientPattern(pattern);
27
+ const backgroundGradient = gradient
28
+ ? `linear-gradient(${gradient.deg || 45}deg, ${gradient.colors.join(", ")})`
29
+ : undefined;
30
+ ---
31
+
32
+ <div class:list={["naut-background"]}>
33
+ {gradient && <div class:list={["naut-background-gradient", gradient.type]}></div>}
34
+ {pattern && <div class:list={["naut-background-pattern", pattern.type]}></div>}
35
+ <slot />
36
+ </div>
37
+
38
+ <style
39
+ define:vars={{
40
+ backgroundColor,
41
+ backgroundImage,
42
+ backgroundSize,
43
+ backgroundPosition,
44
+ backgroundRepeat,
45
+ backgroundOpacity,
46
+ backgroundPattern,
47
+ backgroundGradient
48
+ }}
49
+ >
50
+ .naut-background {
51
+ position: absolute;
52
+ inset: 0;
53
+ z-index: 0;
54
+ background-color: var(--backgroundColor);
55
+ background-image: var(--backgroundImage);
56
+ background-repeat: var(--backgroundRepeat);
57
+ background-position: var(--backgroundPosition);
58
+ background-size: var(--backgroundSize);
59
+ opacity: var(--backgroundOpacity);
60
+ }
61
+
62
+ .naut-background-pattern {
63
+ position: absolute;
64
+ inset: 0;
65
+ z-index: 2;
66
+ background: var(--backgroundPattern);
67
+ }
68
+
69
+ .naut-background-gradient {
70
+ position: absolute;
71
+ inset: 0;
72
+ z-index: 1;
73
+ background: var(--backgroundGradient);
74
+ }
75
+ </style>
@@ -0,0 +1,140 @@
1
+ ---
2
+ import type { Base, Radius } from "../types";
3
+
4
+ export interface BadgeProps extends Base {
5
+ gradient?: { colors: string[]; textColor: string; deg?: number };
6
+ iconOnly?: boolean;
7
+ outlineColor?: string;
8
+ radius?: Radius | "full";
9
+ size?: "sm" | "md" | "lg";
10
+ solid?: { background: string; color: string };
11
+ variant?: "default" | "solid" | "outline" | "dot" | "flat";
12
+ }
13
+
14
+ const {
15
+ size = "md",
16
+ solid = { background: "darkorchid", color: "white" },
17
+ gradient,
18
+ radius = "sm",
19
+ variant = "default",
20
+ outlineColor,
21
+ iconOnly = false,
22
+ ...rest
23
+ } = Astro.props as BadgeProps;
24
+
25
+ const gradientBg = gradient
26
+ ? `linear-gradient(${gradient.deg || 45}deg, ${gradient.colors.join(", ")})`
27
+ : "none";
28
+ ---
29
+
30
+ <div
31
+ class:list={[
32
+ "naut-badge",
33
+ `variant--${variant}`,
34
+ `radius--${radius}`,
35
+ `size--${size}`,
36
+ gradient && "color--gradient",
37
+ iconOnly && "icon-only"]}
38
+ {...rest}
39
+ >
40
+ <slot />
41
+ </div>
42
+ <style
43
+ define:vars={{
44
+ solidBackground: solid.background,
45
+ solidColor: solid.color,
46
+ gradientBg,
47
+ gradientColor: gradient?.textColor,
48
+ outlineColor: outlineColor ?? "var(--naut-color-content)",
49
+ outlineBg: outlineColor ? `rgb(from ${outlineColor} r g b / 0.15)` : "transparent",
50
+ }}
51
+ >
52
+ .naut-badge {
53
+ --outline-color: var(--outlineColor);
54
+ display: inline-flex;
55
+ gap: 0.5rem;
56
+ align-items: center;
57
+ justify-content: center;
58
+ text-transform: uppercase;
59
+ text-wrap: nowrap;
60
+ border: 1px solid var(--naut-color-border);
61
+
62
+ &.variant--solid {
63
+ color: var(--solidColor);
64
+ background-color: var(--solidBackground);
65
+ border-color: var(--solidBackground);
66
+ }
67
+
68
+ &.color--gradient {
69
+ color: var(--gradientColor);
70
+ background: var(--gradientBg);
71
+ }
72
+
73
+ &.variant--outline {
74
+ color: var(--outline-color);
75
+ background-color: var(--outlineBg);
76
+ border: 1px solid var(--outline-color);
77
+ }
78
+
79
+ &.variant--flat {
80
+ background-color: var(--naut-color-base-200);
81
+ border: none;
82
+ }
83
+
84
+ &.variant--dot {
85
+ &::before {
86
+ display: inline-block;
87
+ width: 0.5rem;
88
+ height: 0.5rem;
89
+ content: "";
90
+ background-color: var(--naut-color-content);
91
+ border-radius: 50%;
92
+ }
93
+ }
94
+
95
+ &.radius--sm {
96
+ border-radius: var(--naut-border-radius-sm);
97
+ }
98
+
99
+ &.radius--md {
100
+ border-radius: var(--naut-border-radius-md);
101
+ }
102
+
103
+ &.radius--lg {
104
+ border-radius: var(--naut-border-radius-lg);
105
+ }
106
+
107
+ &.radius--full {
108
+ padding: 0.25rem 0.95rem;
109
+ border-radius: 50rem;
110
+ }
111
+
112
+ &.icon-only {
113
+ aspect-ratio: 1 / 1;
114
+ }
115
+
116
+ &.size--sm {
117
+ padding: 0.03rem 0.65rem;
118
+ font-size: 0.75rem;
119
+ &.icon-only {
120
+ padding: 0.65rem;
121
+ }
122
+ }
123
+
124
+ &.size--md {
125
+ padding: 0.2rem 0.85rem;
126
+ font-size: 0.875rem;
127
+ &.icon-only {
128
+ padding: 0.85rem;
129
+ }
130
+ }
131
+
132
+ &.size--lg {
133
+ padding: 0.35rem 1.15rem;
134
+ font-size: 1.125rem;
135
+ &.icon-only {
136
+ padding: 1.15rem;
137
+ }
138
+ }
139
+ }
140
+ </style>
@@ -0,0 +1,37 @@
1
+ ---
2
+ import { Base } from "../types";
3
+
4
+ export interface BentoProps extends Base {
5
+ columns?: number;
6
+ gap?: string;
7
+ rows?: number;
8
+ }
9
+
10
+ const {
11
+ rows = 4,
12
+ columns = 4,
13
+ gap = "1rem",
14
+ class: className,
15
+ ...rest
16
+ } = Astro.props as BentoProps;
17
+ ---
18
+
19
+ <div class:list={["naut-bento", className]} {...rest}><slot /></div>
20
+ <style define:vars={{ bentoColumns: columns, bentoRows: rows, bentoGap: gap }}>
21
+ .naut-bento {
22
+ --naut-bento-cols: var(--bentoColumns);
23
+ --naut-bento-rows: var(--bentoRows);
24
+ display: grid;
25
+ grid-template-rows: repeat(var(--naut-bento-rows), 1fr);
26
+ grid-template-columns: repeat(var(--naut-bento-cols), 1fr);
27
+ gap: var(--bentoGap);
28
+ aspect-ratio: var(--naut-bento-cols) / var(--naut-bento-rows);
29
+ }
30
+
31
+ @media screen and (aspect-ratio < 1) {
32
+ .naut-bento {
33
+ --naut-bento-cols: var(--bentoRows);
34
+ --naut-bento-rows: var(--bentoColumns);
35
+ }
36
+ }
37
+ </style>
@@ -0,0 +1,26 @@
1
+ ---
2
+ import { Base } from "../types";
3
+
4
+ export interface BentoItemProps extends Base {
5
+ col?: number;
6
+ row?: number;
7
+ }
8
+
9
+ const {
10
+ row = 1,
11
+ col = 1,
12
+ class: className,
13
+ ...rest
14
+ } = Astro.props as BentoItemProps;
15
+ ---
16
+
17
+ <div class:list={["naut-bento-item", className]} {...rest}><slot /></div>
18
+ <style define:vars={{ bentoItemRows: row, bentoItemColumns: col }}>
19
+ .naut-bento-item {
20
+ position: relative;
21
+ grid-row: span var(--bentoItemRows);
22
+ grid-column: span var(--bentoItemColumns);
23
+ overflow: hidden;
24
+ background: var(--naut-color-base-100);
25
+ }
26
+ </style>