@adaptive-sm/astro-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.
Files changed (86) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +183 -0
  3. package/lib/button/Button.astro +58 -0
  4. package/lib/button/buttonCva.ts +196 -0
  5. package/lib/button/buttonIconCva.ts +52 -0
  6. package/lib/button/classesButtonClickAnimation.ts +8 -0
  7. package/lib/button/classesButtonClickAnimationPush.ts +6 -0
  8. package/lib/button/classesButtonClickAnimationSquish.ts +6 -0
  9. package/lib/button/classesButtonDisabled.ts +1 -0
  10. package/lib/card/CardWrapper.astro +15 -0
  11. package/lib/card/classesCardWrapper.ts +16 -0
  12. package/lib/details/Details.astro +57 -0
  13. package/lib/dev/TailwindIndicator.astro +28 -0
  14. package/lib/form/Fieldset.astro +21 -0
  15. package/lib/form/classesFieldset.ts +1 -0
  16. package/lib/generate_ai_rules/generate_agent_rules.bash +9 -0
  17. package/lib/generate_ai_rules/generate_agent_rules_1_lib.bash +33 -0
  18. package/lib/generate_ai_rules/generate_agent_rules_2_copy.bash +14 -0
  19. package/lib/generate_ai_rules/generate_agent_rules_3_combine.bash +31 -0
  20. package/lib/generate_demo_list/DemoList.astro +31 -0
  21. package/lib/generate_demo_list/DemoListType.ts +1 -0
  22. package/lib/generate_demo_list/generateDemoList.ts +55 -0
  23. package/lib/generate_image_list/generateImageList.ts +101 -0
  24. package/lib/grid/classesGridCols.ts +86 -0
  25. package/lib/icon/Icon1.astro +21 -0
  26. package/lib/img/ImageType.ts +6 -0
  27. package/lib/img/Img.astro +27 -0
  28. package/lib/img/TypedImg.astro +26 -0
  29. package/lib/img/classInvertDiagram.ts +1 -0
  30. package/lib/img/classesImgZoomInOnHover.ts +1 -0
  31. package/lib/layouts/MarkdownWrapper.astro +17 -0
  32. package/lib/layouts/MinimalLayout.astro +67 -0
  33. package/lib/layouts/parts/ThemeToggle.astro +137 -0
  34. package/lib/layouts/parts/astroElementId.ts +7 -0
  35. package/lib/layouts/parts/markdown.css +93 -0
  36. package/lib/link/LinkButton.astro +48 -0
  37. package/lib/link/LinkText.astro +23 -0
  38. package/lib/link/classesTextLink.ts +13 -0
  39. package/lib/list/BlackBulletPoint.astro +16 -0
  40. package/lib/list/BlackBulletPoints.astro +23 -0
  41. package/lib/list/CheckPoint.astro +20 -0
  42. package/lib/list/CheckPoints.astro +23 -0
  43. package/lib/list/NumberedList.astro +14 -0
  44. package/lib/list/Ps.astro +12 -0
  45. package/lib/list/TextOrLink.astro +19 -0
  46. package/lib/modal/Modal.astro +54 -0
  47. package/lib/modal/Modal.module.css +19 -0
  48. package/lib/modal/ModalButton.astro +41 -0
  49. package/lib/modal/modal.ts +137 -0
  50. package/lib/page/PageCentered.astro +14 -0
  51. package/lib/page/PageCenteredCard.astro +17 -0
  52. package/lib/page/classesBg.ts +27 -0
  53. package/lib/page/classesPageCentered.ts +6 -0
  54. package/lib/popover/Popover1.astro +45 -0
  55. package/lib/popover/setupPopoverListeners.ts +22 -0
  56. package/lib/select/Select.astro +45 -0
  57. package/lib/table/DesktopTableClassses.ts +6 -0
  58. package/lib/table/MobileTableClassses.ts +7 -0
  59. package/lib/table/Table.astro +41 -0
  60. package/lib/table/TableColumnDef.ts +10 -0
  61. package/lib/table/TableD.astro +55 -0
  62. package/lib/table/TableM.astro +22 -0
  63. package/lib/table/TableMEntry.astro +27 -0
  64. package/lib/table/sharedTableRowClasses.ts +1 -0
  65. package/lib/table/tableVisibilityClasses.ts +28 -0
  66. package/lib/text/classesTextGray.ts +1 -0
  67. package/lib/text/classesTextHeader.ts +1 -0
  68. package/lib/utils/bun/BunCmd.ts +7 -0
  69. package/lib/utils/bun/cryAndTryAgainLater.ts +6 -0
  70. package/lib/utils/bun/logBunCmd.ts +1 -0
  71. package/lib/utils/bun/runCmdAsync.ts +44 -0
  72. package/lib/utils/bun/runCmdLocally.ts +13 -0
  73. package/lib/utils/obj/objectKeys.ts +21 -0
  74. package/lib/utils/ran/generateId12.ts +7 -0
  75. package/lib/utils/ran/generateId3.ts +7 -0
  76. package/lib/utils/ran/generateId4.ts +7 -0
  77. package/lib/utils/ran/generateId5.ts +7 -0
  78. package/lib/utils/ran/generateId6.ts +7 -0
  79. package/lib/utils/ran/generateId7.ts +7 -0
  80. package/lib/utils/ran/generateReadableId.ts +35 -0
  81. package/lib/utils/ran/urlAlphabet32.ts +8 -0
  82. package/lib/utils/ui/classArr.ts +3 -0
  83. package/lib/utils/ui/classMerge.ts +10 -0
  84. package/lib/utils/ui/isDevEnv.ts +7 -0
  85. package/lib/utils/ui/tailwindBreakpoint.ts +83 -0
  86. package/package.json +56 -0
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+
2
+ MIT License
3
+
4
+ Copyright (c) 2025 Adaptive Shield Matrix
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,183 @@
1
+ # Adaptive Astro UI
2
+
3
+ A library of reusable UI components for Astro projects. Built with TypeScript, Tailwind CSS, and Astro in static output mode. Components are designed to be accessible, customizable, and easy to integrate.
4
+
5
+ ## Installation
6
+
7
+ Install the package using Bun:
8
+
9
+ ```bash
10
+ bun add @adaptive-sm/astro-ui
11
+ ```
12
+
13
+ ## Recommended configuration
14
+
15
+ In your `astro.config.mjs`, set up the `~` alias to point to the library:
16
+
17
+ ```js
18
+ import { defineConfig } from 'astro/config';
19
+
20
+ export default defineConfig({
21
+ vite: {
22
+ resolve: {
23
+ alias: {
24
+ '~': new URL('./node_modules/@adaptive-sm/astro-ui/lib', import.meta.url).pathname,
25
+ },
26
+ },
27
+ },
28
+ });
29
+ ```
30
+
31
+ ## Tailwind CSS Configuration
32
+
33
+ To ensure Tailwind scans the library's source files for classes (since components are published as source without a build step), add the following `@source` directive to your project's `src/layouts/global.css` (or equivalent global stylesheet):
34
+
35
+ ```css
36
+ @source '/node_modules/@adaptive-sm/astro-ui/lib/**/*.{astro,html,md,mdx,ts,tsx}';
37
+ ```
38
+
39
+ This tells Tailwind to include classes from the library's `.astro`, `.ts`, and other relevant files in the purge process, preventing unused classes from being purged during the build. Without it, Tailwind might not detect classes used in imported components, leading to missing styles.
40
+
41
+ ## Usage
42
+
43
+ ## No Build Step Required
44
+
45
+ This library ships source `.astro` and `.ts` files directly—no pre-build needed. Benefits include:
46
+
47
+ - **Smaller size**: No bundled or compiled files.
48
+ - **Full compatibility**: Your Astro project compiles components using its own Astro, Vite, and Tailwind setup.
49
+ - **Better DX**: Preserves TypeScript types and IntelliSense.
50
+ - **Easier updates**: Consumers get changes immediately, with no build artifacts to manage.
51
+
52
+ Just set up your alias - Astro handles the rest automatically.
53
+
54
+ Import and use components directly in your Astro files. For example:
55
+
56
+ ### Button Component
57
+
58
+ ```astro
59
+ ---
60
+ import { Button } from '~/button/Button.astro';
61
+ ---
62
+
63
+ <Button variant="primary">Click me</Button>
64
+ ```
65
+
66
+ ### Card Component
67
+
68
+ ```astro
69
+ ---
70
+ import { CardWrapper } from '~/card/CardWrapper.astro';
71
+ ---
72
+
73
+ <CardWrapper>
74
+ Card content here.
75
+ </CardWrapper>
76
+ ```
77
+
78
+ ### Image Component
79
+
80
+ ```astro
81
+ ---
82
+ import { Img } from '~/img/Img.astro';
83
+ ---
84
+
85
+ <Img src="/path/to/image.jpg" alt="Description" />
86
+ ```
87
+
88
+ Refer to individual component documentation in the source code for props and variants.
89
+
90
+ ## Components
91
+
92
+ ### Buttons
93
+ - [Button.astro](lib/button/Button.astro)
94
+ - Button variants and animations via CVAs in `buttonCva.ts`, `buttonIconCva.ts`
95
+
96
+ ### Cards
97
+ - [CardWrapper.astro](lib/card/CardWrapper.astro)
98
+
99
+ ### Details
100
+ - [Details.astro](lib/details/Details.astro)
101
+
102
+ ### Forms
103
+ - [Fieldset.astro](lib/form/Fieldset.astro)
104
+
105
+ ### Icons
106
+ - [Icon1.astro](lib/icon/Icon1.astro) (replaces SVG icons)
107
+
108
+ ### Images
109
+ - [Img.astro](lib/img/Img.astro)
110
+ - [TypedImg.astro](lib/img/TypedImg.astro)
111
+
112
+ ### Layouts
113
+ - [MinimalLayout.astro](lib/layouts/MinimalLayout.astro)
114
+ - [MarkdownWrapper.astro](lib/layouts/MarkdownWrapper.astro)
115
+ - [ThemeToggle.astro](lib/layouts/parts/ThemeToggle.astro)
116
+
117
+ ### Links
118
+ - [LinkText.astro](lib/link/LinkText.astro)
119
+ - [LinkButton.astro](lib/link/LinkButton.astro)
120
+
121
+ ### Lists
122
+ - [BlackBulletPoints.astro](lib/list/BlackBulletPoints.astro)
123
+ - [CheckPoints.astro](lib/list/CheckPoints.astro)
124
+ - [NumberedList.astro](lib/list/NumberedList.astro)
125
+
126
+ ### Modals
127
+ - [ModalButton.astro](lib/modal/ModalButton.astro)
128
+
129
+ ### Pages
130
+ - [PageCentered.astro](lib/page/PageCentered.astro)
131
+ - [PageCenteredCard.astro](lib/page/PageCenteredCard.astro)
132
+
133
+ ### Popovers
134
+ - [Popover1.astro](lib/popover/Popover1.astro)
135
+
136
+ ### Selects
137
+ - [Select.astro](lib/select/Select.astro)
138
+
139
+ ### Table
140
+ - [Table.astro](lib/table/Table.astro)
141
+
142
+ ## Demos
143
+
144
+ Explore component demos at:
145
+ [https://adaptive-astro-ui.pages.dev/](https://adaptive-astro-ui.pages.dev/)
146
+
147
+ ## Development
148
+
149
+ ### Building the Library
150
+
151
+ ```bash
152
+ bun run build
153
+ ```
154
+
155
+ ### Running Demos
156
+
157
+ ```bash
158
+ bun run dev
159
+ ```
160
+
161
+ ### Linting and Formatting
162
+
163
+ Uses Biome for linting and formatting:
164
+
165
+ ```bash
166
+ bun run biome check .
167
+ bun run biome format --write .
168
+ ```
169
+
170
+ ## Contributing
171
+
172
+ 1. Fork the repository.
173
+ 2. Create a feature branch.
174
+ 3. Add or update components in `lib/`.
175
+ 4. Update demos in `src/pages/demos/`.
176
+ 5. Ensure tests pass and run `bun run build`.
177
+ 6. Submit a pull request.
178
+
179
+ Follow the coding rules in `.roo/rules-code/` for style and best practices.
180
+
181
+ ## License
182
+
183
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,58 @@
1
+ ---
2
+ import { buttonCva2, type ButtonSize, type ButtonVariant } from "~/button/buttonCva"
3
+ import { buttonIconCva } from "~/button/buttonIconCva"
4
+ import { classesButtonClickAnimation } from "~/button/classesButtonClickAnimation"
5
+ import Icon1 from "~/icon/Icon1.astro"
6
+ import { classMerge } from "~/utils/ui/classMerge"
7
+
8
+ interface Props {
9
+ id?: string
10
+ textId?: string
11
+ contentId?: string
12
+ contentClass?: string
13
+ title?: string
14
+ text?: string
15
+ variant: ButtonVariant
16
+ size?: ButtonSize
17
+ icon?: string
18
+ iconRight?: string
19
+ iconClass?: string
20
+ class?: string
21
+ onclick?: any
22
+ }
23
+ const props = Astro.props
24
+
25
+ const classes = buttonCva2(props.variant, props.size, classesButtonClickAnimation, props.class)
26
+ const hasChildren = Astro.slots.has("default")
27
+ const hasContent = hasChildren || props.text
28
+ ---
29
+
30
+ <button id={props.id} title={props.title} class={classes} onclick={props.onclick}>
31
+ {
32
+ props.icon && (
33
+ <Icon1
34
+ path={props.icon}
35
+ class={classMerge(hasContent && "mr-2", buttonIconCva(props.variant, props.iconClass))}
36
+ />
37
+ )
38
+ }
39
+ {
40
+ props.text && (
41
+ <span id={props.contentId} class={props.contentClass}>
42
+ {props.text}
43
+ </span>
44
+ )
45
+ }
46
+ {
47
+ hasContent && (
48
+ <span id={props.contentId} class={props.contentClass}>
49
+ <slot />
50
+ </span>
51
+ )
52
+ }
53
+ {
54
+ props.iconRight && (
55
+ <Icon1 path={props.iconRight} class={buttonIconCva(props.variant, hasContent && "ml-2", props.iconClass)} />
56
+ )
57
+ }
58
+ </button>
@@ -0,0 +1,196 @@
1
+ import { classArr } from "~/utils/ui/classArr"
2
+ import { classMerge } from "~/utils/ui/classMerge"
3
+ import { classesButtonClickAnimation } from "./classesButtonClickAnimation"
4
+ import { classesButtonDisabled } from "./classesButtonDisabled"
5
+
6
+ export type ButtonVariant = keyof typeof buttonVariant
7
+ export const buttonVariant = {
8
+ // transparent bg
9
+ outline: "outline",
10
+ ghost: "ghost",
11
+ link: "link",
12
+ // filled black/white/gray
13
+ filled: "filled",
14
+ subtle: "subtle",
15
+ contrast: "contrast",
16
+ // filled colors
17
+ filledYellow: "filledYellow",
18
+ filledAmber: "filledAmber",
19
+ filledOrange: "filledOrange",
20
+ filledRed: "filledRed",
21
+ filledGreen: "filledGreen",
22
+ filledSky: "filledSky",
23
+ filledIndigo: "filledIndigo",
24
+ // outlined colors
25
+ outlineRed: "outlineRed",
26
+ } as const
27
+
28
+ export type ButtonSize = keyof typeof buttonSize
29
+ export const buttonSize = {
30
+ none: "none",
31
+ minimal: "minimal",
32
+ sm: "sm",
33
+ default: "default",
34
+ lg: "lg",
35
+ } as const
36
+
37
+ export type ButtonCvaProps = {
38
+ variant?: ButtonVariant
39
+ size?: ButtonSize
40
+ }
41
+
42
+ const baseClasses = classArr(
43
+ "inline-flex", // layout
44
+ "font-medium", // text
45
+ "items-center justify-center", // layout children
46
+ "rounded-md ring-offset-background", // rounded, rings
47
+ "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", // focus
48
+ "transition-colors", // animation
49
+ "group",
50
+ "cursor-pointer",
51
+ )
52
+
53
+ const variantClasses = {
54
+ //
55
+ // transparent bg
56
+ //
57
+ outline: classArr(
58
+ "bg-transparent dark:bg-transparent",
59
+ "dark:text-slate-100", // text
60
+ "hover:bg-slate-100", // bg hover
61
+ "border border-slate-200 dark:border-slate-700 dark:hover:bg-slate-900", // border
62
+ ),
63
+ ghost: classArr(
64
+ "dark:text-slate-100 dark:hover:text-slate-100", // text
65
+ "bg-transparent dark:bg-transparent", // bg
66
+ "data-[state=open]:bg-transparent dark:data-[state=open]:bg-transparent", // bg data
67
+ "hover:bg-slate-100 dark:hover:bg-slate-800", // bg hover
68
+ ),
69
+ link: classArr(
70
+ "text-slate-900 dark:text-slate-100", // text
71
+ "underline-offset-4 hover:underline ", // underline
72
+ "bg-transparent dark:bg-transparent", // bg
73
+ "hover:bg-transparent dark:hover:bg-transparent", // bg hover
74
+ ),
75
+ //
76
+ // filled grayscale
77
+ //
78
+ filled: classArr(
79
+ "dark:text-slate-100", // text
80
+ "bg-white dark:bg-black", // bg
81
+ "hover:bg-slate-50 dark:hover:bg-slate-900", // bg hover
82
+ ),
83
+ subtle: classArr(
84
+ "text-slate-900 dark:text-slate-100", // text
85
+ "bg-slate-100 dark:bg-slate-700", // bg
86
+ "hover:bg-slate-200 dark:hover:bg-slate-600", // bg hover
87
+ ),
88
+ contrast: classArr(
89
+ "text-white dark:text-slate-900 dark:hover:text-slate-900", // text
90
+ "bg-slate-900 dark:bg-slate-50", // bg
91
+ "hover:bg-slate-700 dark:hover:bg-slate-300", // bg hover
92
+ ),
93
+ //
94
+ // filled colors
95
+ //
96
+ filledYellow: classArr(
97
+ "text-white dark:text-yellow-100 ", // text
98
+ "bg-yellow-500 dark:bg-yellow-800 ", // bg
99
+ "hover:bg-yellow-700 dark:hover:bg-yellow-600", // bg hover
100
+ "focus:ring-yellow-400 dark:focus:ring-yellow-400", // focus
101
+ ),
102
+ filledAmber: classArr(
103
+ "text-white dark:text-amber-100 ", // text
104
+ "bg-amber-500 dark:bg-amber-800 ", // bg
105
+ "hover:bg-amber-700 dark:hover:bg-amber-600", // bg hover
106
+ "focus:ring-amber-400 dark:focus:ring-amber-400", // focus
107
+ ),
108
+ filledOrange: classArr(
109
+ "text-white dark:text-orange-100 ", // text
110
+ "bg-orange-500 dark:bg-orange-800 ", // bg
111
+ "hover:bg-orange-700 dark:hover:bg-orange-600", // bg hover
112
+ "focus:ring-orange-400 dark:focus:ring-orange-400", // focus
113
+ ),
114
+ filledRed: classArr(
115
+ "text-white", // text
116
+ "bg-red-500 dark:bg-red-700", // bg
117
+ "hover:bg-red-600 dark:hover:bg-red-600", // bg hover
118
+ "focus:ring-red-400 dark:focus:ring-red-400", // focus
119
+ ),
120
+ filledGreen: classArr(
121
+ "text-white", // text
122
+ "bg-green-500 hover:bg-green-700 dark:hover:bg-green-700", // bg
123
+ "focus:ring-green-400 dark:focus:ring-green-400", // focus
124
+ ),
125
+ filledSky: classArr(
126
+ "text-white", // text
127
+ "bg-sky-500 hover:bg-sky-700 dark:hover:bg-sky-700", // bg
128
+ "focus:ring-sky-400 dark:focus:ring-sky-400", // focus
129
+ ),
130
+ filledIndigo: classArr(
131
+ "text-white dark:text-indigo-100 ", // text
132
+ "bg-indigo-500 dark:bg-indigo-800 ", // bg
133
+ "hover:bg-indigo-700 dark:hover:bg-indigo-600", // bg hover
134
+ "focus:ring-indigo-400 dark:focus:ring-indigo-400", // focus
135
+ ),
136
+ //
137
+ // outline toast colors
138
+ //
139
+ outlineRed: classArr(
140
+ "text-red-500 dark:text-red-500", // text
141
+ "border border-red-200 dark:border-red-700", // border
142
+ "bg-transparent", // bg
143
+ "hover:bg-red-100 dark:hover:bg-red-950", // bg hover
144
+ "focus:ring-red-400 dark:focus:ring-red-400", // focus
145
+ ),
146
+ } as const satisfies Record<ButtonVariant, string>
147
+
148
+ const sizeClasses = {
149
+ none: "",
150
+ minimal: "rounded-none",
151
+ sm: "px-3",
152
+ default: "py-2 px-3",
153
+ lg: "px-8 py-4 text-lg",
154
+ } as const satisfies Record<ButtonSize, string>
155
+
156
+ const defaultSize = buttonSize.default
157
+
158
+ export function buttonCva1(variant: ButtonVariant, ...customClasses: (string | boolean | undefined | null | 0)[]) {
159
+ return buttonCva2(variant, null, ...customClasses)
160
+ }
161
+
162
+ export function buttonCva2(
163
+ variant: ButtonVariant,
164
+ size: ButtonSize | null = defaultSize,
165
+ ...customClasses: (string | boolean | undefined | null | 0)[]
166
+ ) {
167
+ const v = variant
168
+ const s = size ?? defaultSize
169
+ return classMerge(baseClasses, variantClasses[v], sizeClasses[s], combinedClasses(v, s), customClasses)
170
+ }
171
+
172
+ function combinedClasses(variant: ButtonVariant, size: ButtonSize) {
173
+ const variantGroup1 =
174
+ variant === buttonVariant.outline || variant === buttonVariant.filledYellow || variant === buttonVariant.outlineRed
175
+ if (variantGroup1 && size === buttonSize.lg) {
176
+ return "border-2"
177
+ }
178
+ return null
179
+ }
180
+
181
+ export function buttonCvaIconOnly(
182
+ variant: ButtonVariant,
183
+ isLoading: boolean | undefined,
184
+ isDisabled: boolean | undefined,
185
+ ...customClasses: (string | boolean | undefined | null | 0)[]
186
+ ) {
187
+ const classes = buttonCva2(
188
+ variant,
189
+ buttonSize.none,
190
+ classesButtonClickAnimation,
191
+ "rounded-full p-2.5",
192
+ (isDisabled || isLoading) && classesButtonDisabled,
193
+ ...customClasses,
194
+ )
195
+ return classes
196
+ }
@@ -0,0 +1,52 @@
1
+ import { classArr } from "~/utils/ui/classArr"
2
+ import { classMerge } from "~/utils/ui/classMerge"
3
+ import { type ButtonVariant } from "./buttonCva"
4
+
5
+ const classesTextFillBlack = "text-black fill-black"
6
+ const classesTextFillWhite = "text-white fill-white"
7
+
8
+ const classesBlackWhite = classArr(
9
+ classesTextFillBlack,
10
+ "dark:text-white", // dark text
11
+ "dark:fill-white", // dark fill
12
+ )
13
+
14
+ const classesWhiteWhite = classArr(
15
+ classesTextFillWhite,
16
+ "dark:text-white", // dark text
17
+ "dark:fill-white", // dark fill
18
+ )
19
+
20
+ const baseClasses = classArr(
21
+ "size-6", // size
22
+ )
23
+
24
+ const buttonIconClasses = {
25
+ // transparent bg
26
+ outline: classesBlackWhite,
27
+ ghost: classesBlackWhite,
28
+ link: classesBlackWhite,
29
+ // filled grayscale
30
+ filled: classesBlackWhite,
31
+ subtle: classesBlackWhite,
32
+ contrast: classArr(classesTextFillWhite, "dark:text-black dark:fill-black"),
33
+ // filled colors
34
+ filledYellow: classArr(classesTextFillWhite, "dark:text-yellow-100 dark:fill-yellow-100"),
35
+ filledOrange: classArr(classesTextFillWhite, "dark:text-orange-100 dark:fill-orange-100"),
36
+ filledAmber: classArr(classesTextFillWhite, "dark:text-amber-100 dark:fill-amber-100"),
37
+ filledRed: classArr(classesTextFillWhite, "dark:text-red-100 dark:fill-red-100"),
38
+
39
+ filledGreen: classesWhiteWhite,
40
+ filledIndigo: classArr(classesTextFillWhite, "dark:text-indigo-100 dark:fill-indigo-100"),
41
+ // outlineRed: classesWhiteWhite,
42
+ filledSky: classesWhiteWhite,
43
+ // outlined colors
44
+ outlineRed: classArr("text-red-500 fill-red-500 dark:text-red-700 dark:fill-red-700"),
45
+ } as const satisfies Record<ButtonVariant, string>
46
+
47
+ export function buttonIconCva(
48
+ variant: ButtonVariant,
49
+ ...customClasses: (string | boolean | undefined | null | 0 | 0n)[]
50
+ ) {
51
+ return classMerge(baseClasses, buttonIconClasses[variant], customClasses)
52
+ }
@@ -0,0 +1,8 @@
1
+ import { classArr } from "~/utils/ui/classArr"
2
+
3
+ export const classesButtonClickAnimation = classArr(
4
+ // "duration-300",
5
+ "duration-0",
6
+ // "[transition-timing-function:cubic-bezier(0.175,0.885,0.32,1.275)]",
7
+ "active:scale-95",
8
+ )
@@ -0,0 +1,6 @@
1
+ import { classArr } from "~/utils/ui/classArr"
2
+
3
+ /**
4
+ * disable hover because popovers move with it
5
+ */
6
+ export const classesButtonClickAnimationPush = classArr("hover:-translate-y-px")
@@ -0,0 +1,6 @@
1
+ import { classArr } from "~/utils/ui/classArr"
2
+
3
+ /**
4
+ * unused because it's a little to fancy
5
+ */
6
+ export const classesButtonClickAnimationSquish = classArr("active:translate-y-1 active:scale-x-105 active:scale-y-95")
@@ -0,0 +1 @@
1
+ export const classesButtonDisabled = "cursor-not-allowed opacity-70"
@@ -0,0 +1,15 @@
1
+ ---
2
+ import { classesCardWrapperP4 } from "~/card/classesCardWrapper"
3
+ import { classArr } from "~/utils/ui/classArr"
4
+ import { classMerge } from "~/utils/ui/classMerge"
5
+
6
+ interface Props {
7
+ id?:string
8
+ class?: string
9
+ }
10
+ const p = Astro.props
11
+ ---
12
+
13
+ <div id={p.id} class={classMerge(classesCardWrapperP4, p.class)}>
14
+ <slot />
15
+ </div>
@@ -0,0 +1,16 @@
1
+ import { classArr } from "~/utils/ui/classArr"
2
+
3
+ export const classesCardWrapper = classArr(
4
+ "rounded-lg shadow-lg", // card shadows/padding
5
+ "bg-white dark:bg-gray-900", // bg
6
+ )
7
+
8
+ export const classesCardWrapperP4 = classArr(classesCardWrapper, "p-4 lg:p-8")
9
+ export const classesCardWrapperP8 = classArr(classesCardWrapper, "p-4 sm:p-8")
10
+
11
+ export const classesCardWrapperPage = classArr(
12
+ "rounded-xl shadow-xl", // rounded border + shadow
13
+ "p-4 sm:p-8 md:p-12", // padding
14
+ // "sm:mx-auto", // center
15
+ "bg-white dark:bg-gray-900", // bg
16
+ )
@@ -0,0 +1,57 @@
1
+ ---
2
+ import Icon1 from "~/icon/Icon1.astro"
3
+ import { classArr } from "~/utils/ui/classArr"
4
+ import { classMerge } from "~/utils/ui/classMerge"
5
+ import { mdiChevronDown } from "@mdi/js"
6
+
7
+ interface Props {
8
+ icon?: string
9
+ title?: string
10
+ subtitle?: string
11
+ class?: string
12
+ summaryClass?: string
13
+ }
14
+ const props = Astro.props
15
+ const icon = props.icon
16
+ const title = props.title
17
+ const subtitle = props.subtitle
18
+ const summaryClass = props.summaryClass
19
+ const hasSummary = Astro.slots.has("summary")
20
+ ---
21
+
22
+ <details
23
+ class={classMerge(
24
+ "group",
25
+ "bg-white dark:bg-gray-800",
26
+ "rounded-lg border border-gray-200",
27
+ "shadow-sm",
28
+ "overflow-hidden",
29
+ props.class,
30
+ )}
31
+ >
32
+ <summary
33
+ class={classArr(
34
+ "flex flex-col sm:flex-row items-center justify-between gap-4",
35
+ "p-6",
36
+ "cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors",
37
+ )}
38
+ >
39
+ {
40
+ !hasSummary && title && (
41
+ <>
42
+ {icon && <Icon1 path={icon} class="size-7 mt-1" />}
43
+ <div class="flex-1">
44
+ <h3 class={"text-xl font-semibold"}>{title}</h3>
45
+ {subtitle && <p class={"text-muted-foreground mt-1"}>{subtitle}</p>}
46
+ </div>
47
+ </>
48
+ )
49
+ }
50
+ <slot name="summary" />
51
+ <Icon1
52
+ path={mdiChevronDown}
53
+ class={classArr("size-7", "text-gray-400 dark:text-gray-600", "transition-transform group-open:rotate-180")}
54
+ />
55
+ </summary>
56
+ <slot />
57
+ </details>
@@ -0,0 +1,28 @@
1
+ ---
2
+ import { classMerge } from "~/utils/ui/classMerge"
3
+ import { isDevEnv } from "~/utils/ui/isDevEnv"
4
+ ---
5
+
6
+ {
7
+ isDevEnv() && (
8
+ <div
9
+ id={"tailwindIndicator"}
10
+ class={classMerge(
11
+ "fixed bottom-1 left-1 z-50", // position
12
+ "flex size-6 items-center justify-center", // size
13
+ "rounded-full p-3", // size
14
+ "font-mono font-bold text-sm", // font
15
+ "bg-zinc-100 dark:bg-zinc-900", // bg
16
+ "select-none print:hidden",
17
+ )}
18
+ >
19
+ <div class="block sm:hidden">xs</div>
20
+ <div class="hidden sm:block md:hidden lg:hidden xl:hidden 2xl:hidden 3xl:hidden">sm</div>
21
+ <div class="hidden md:block lg:hidden xl:hidden 2xl:hidden 3xl:hidden">md</div>
22
+ <div class="hidden lg:block xl:hidden 2xl:hidden 3xl:hidden">lg</div>
23
+ <div class="hidden xl:block 2xl:hidden 3xl:hidden">xl</div>
24
+ <div class="hidden 2xl:block 3xl:hidden">2xl</div>
25
+ <div class="hidden 3xl:block">3xl</div>
26
+ </div>
27
+ )
28
+ }
@@ -0,0 +1,21 @@
1
+ ---
2
+ import { classesFieldset } from "./classesFieldset"
3
+ import { classMerge } from "~/utils/ui/classMerge"
4
+
5
+ interface Props {
6
+ title: string
7
+ titleClass?: string
8
+ subtitles?: string | string[]
9
+ subtitleClass?: string
10
+ class?: string
11
+ }
12
+ const props = Astro.props
13
+ ---
14
+
15
+ <fieldset class={classMerge(classesFieldset, props.class)}>
16
+ <legend class={classMerge("px-1", props.titleClass)}>
17
+ {props.title}
18
+ <!--{props.subtitles && <PopoverI title={props.title} points={props.subtitles} />}-->
19
+ </legend>
20
+ <slot />
21
+ </fieldset>
@@ -0,0 +1 @@
1
+ export const classesFieldset = "rounded-lg border border-solid border-gray-300 dark:border-gray-700 p-3"