frutjam 2.3.0-beta.0 → 2.3.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/LICENSE +1 -1
- package/README.github.md +382 -0
- package/README.md +90 -42
- package/README.npm.md +124 -0
- package/dist/base/reboot/index.js +5 -0
- package/dist/base/reboot/object.js +1 -0
- package/dist/base/tokens/index.js +5 -0
- package/dist/base/tokens/object.js +1 -0
- package/dist/base.css +62 -0
- package/dist/base.css.d.ts +1 -0
- package/dist/components/accordion/index.js +7 -0
- package/dist/components/accordion/object.js +1 -0
- package/dist/components/accordion.css +2 -0
- package/dist/components/accordion.css.d.ts +1 -0
- package/dist/components/alert/index.js +7 -0
- package/dist/components/alert/object.js +1 -0
- package/dist/components/alert.css +2 -0
- package/dist/components/alert.css.d.ts +1 -0
- package/dist/components/avatar/index.js +7 -0
- package/dist/components/avatar/object.js +1 -0
- package/dist/components/avatar.css +2 -0
- package/dist/components/avatar.css.d.ts +1 -0
- package/dist/components/badge/index.js +7 -0
- package/dist/components/badge/object.js +1 -0
- package/dist/components/badge.css +2 -0
- package/dist/components/badge.css.d.ts +1 -0
- package/dist/components/breadcrumb/index.js +7 -0
- package/dist/components/breadcrumb/object.js +1 -0
- package/dist/components/breadcrumb.css +2 -0
- package/dist/components/breadcrumb.css.d.ts +1 -0
- package/dist/components/button/index.js +7 -0
- package/dist/components/button/object.js +1 -0
- package/dist/components/button.css +2 -0
- package/dist/components/button.css.d.ts +1 -0
- package/dist/components/card/index.js +7 -0
- package/dist/components/card/object.js +1 -0
- package/dist/components/card.css +2 -0
- package/dist/components/card.css.d.ts +1 -0
- package/dist/components/carousel/index.js +7 -0
- package/dist/components/carousel/object.js +1 -0
- package/dist/components/carousel.css +2 -0
- package/dist/components/carousel.css.d.ts +1 -0
- package/dist/components/chat/index.js +7 -0
- package/dist/components/chat/object.js +1 -0
- package/dist/components/chat.css +2 -0
- package/dist/components/chat.css.d.ts +1 -0
- package/dist/components/checkbox/index.js +7 -0
- package/dist/components/checkbox/object.js +1 -0
- package/dist/components/checkbox.css +2 -0
- package/dist/components/checkbox.css.d.ts +1 -0
- package/dist/components/collapsible/index.js +7 -0
- package/dist/components/collapsible/object.js +1 -0
- package/dist/components/collapsible.css +2 -0
- package/dist/components/collapsible.css.d.ts +1 -0
- package/dist/components/combobox/index.js +7 -0
- package/dist/components/combobox/object.js +1 -0
- package/dist/components/combobox.css +2 -0
- package/dist/components/combobox.css.d.ts +1 -0
- package/dist/components/countdown/index.js +7 -0
- package/dist/components/countdown/object.js +1 -0
- package/dist/components/countdown.css +2 -0
- package/dist/components/countdown.css.d.ts +1 -0
- package/dist/components/diff/index.js +7 -0
- package/dist/components/diff/object.js +1 -0
- package/dist/components/diff.css +2 -0
- package/dist/components/diff.css.d.ts +1 -0
- package/dist/components/divider/index.js +7 -0
- package/dist/components/divider/object.js +1 -0
- package/dist/components/divider.css +2 -0
- package/dist/components/divider.css.d.ts +1 -0
- package/dist/components/drawer/index.js +7 -0
- package/dist/components/drawer/object.js +1 -0
- package/dist/components/drawer.css +2 -0
- package/dist/components/drawer.css.d.ts +1 -0
- package/dist/components/footer/index.js +7 -0
- package/dist/components/footer/object.js +1 -0
- package/dist/components/footer.css +2 -0
- package/dist/components/footer.css.d.ts +1 -0
- package/dist/components/header/index.js +7 -0
- package/dist/components/header/object.js +1 -0
- package/dist/components/header.css +2 -0
- package/dist/components/header.css.d.ts +1 -0
- package/dist/components/hero/index.js +7 -0
- package/dist/components/hero/object.js +1 -0
- package/dist/components/hero.css +2 -0
- package/dist/components/hero.css.d.ts +1 -0
- package/dist/components/indicator/index.js +7 -0
- package/dist/components/indicator/object.js +1 -0
- package/dist/components/indicator.css +2 -0
- package/dist/components/indicator.css.d.ts +1 -0
- package/dist/components/input/index.js +7 -0
- package/dist/components/input/object.js +1 -0
- package/dist/components/input.css +2 -0
- package/dist/components/input.css.d.ts +1 -0
- package/dist/components/join/index.js +7 -0
- package/dist/components/join/object.js +1 -0
- package/dist/components/join.css +2 -0
- package/dist/components/join.css.d.ts +1 -0
- package/dist/components/kbd/index.js +7 -0
- package/dist/components/kbd/object.js +1 -0
- package/dist/components/kbd.css +2 -0
- package/dist/components/kbd.css.d.ts +1 -0
- package/dist/components/link/index.js +7 -0
- package/dist/components/link/object.js +1 -0
- package/dist/components/link.css +2 -0
- package/dist/components/link.css.d.ts +1 -0
- package/dist/components/loading/index.js +7 -0
- package/dist/components/loading/object.js +1 -0
- package/dist/components/loading.css +2 -0
- package/dist/components/loading.css.d.ts +1 -0
- package/dist/components/marquee/index.js +7 -0
- package/dist/components/marquee/object.js +1 -0
- package/dist/components/marquee.css +2 -0
- package/dist/components/marquee.css.d.ts +1 -0
- package/dist/components/mask/index.js +7 -0
- package/dist/components/mask/object.js +1 -0
- package/dist/components/mask.css +2 -0
- package/dist/components/mask.css.d.ts +1 -0
- package/dist/components/menu/index.js +7 -0
- package/dist/components/menu/object.js +1 -0
- package/dist/components/menu.css +2 -0
- package/dist/components/menu.css.d.ts +1 -0
- package/dist/components/modal/index.js +7 -0
- package/dist/components/modal/object.js +1 -0
- package/dist/components/modal.css +2 -0
- package/dist/components/modal.css.d.ts +1 -0
- package/dist/components/navbar/index.js +7 -0
- package/dist/components/navbar/object.js +1 -0
- package/dist/components/navbar.css +2 -0
- package/dist/components/navbar.css.d.ts +1 -0
- package/dist/components/popover/index.js +7 -0
- package/dist/components/popover/object.js +1 -0
- package/dist/components/popover.css +2 -0
- package/dist/components/popover.css.d.ts +1 -0
- package/dist/components/progress/index.js +7 -0
- package/dist/components/progress/object.js +1 -0
- package/dist/components/progress.css +2 -0
- package/dist/components/progress.css.d.ts +1 -0
- package/dist/components/radial-progress/index.js +7 -0
- package/dist/components/radial-progress/object.js +1 -0
- package/dist/components/radial-progress.css +2 -0
- package/dist/components/radial-progress.css.d.ts +1 -0
- package/dist/components/radio/index.js +7 -0
- package/dist/components/radio/object.js +1 -0
- package/dist/components/radio.css +2 -0
- package/dist/components/radio.css.d.ts +1 -0
- package/dist/components/range/index.js +7 -0
- package/dist/components/range/object.js +1 -0
- package/dist/components/range.css +2 -0
- package/dist/components/range.css.d.ts +1 -0
- package/dist/components/rating/index.js +7 -0
- package/dist/components/rating/object.js +1 -0
- package/dist/components/rating.css +2 -0
- package/dist/components/rating.css.d.ts +1 -0
- package/dist/components/select/index.js +7 -0
- package/dist/components/select/object.js +1 -0
- package/dist/components/select.css +2 -0
- package/dist/components/select.css.d.ts +1 -0
- package/dist/components/sidebar/index.js +7 -0
- package/dist/components/sidebar/object.js +1 -0
- package/dist/components/sidebar.css +2 -0
- package/dist/components/sidebar.css.d.ts +1 -0
- package/dist/components/skeleton/index.js +7 -0
- package/dist/components/skeleton/object.js +1 -0
- package/dist/components/skeleton.css +2 -0
- package/dist/components/skeleton.css.d.ts +1 -0
- package/dist/components/stat/index.js +7 -0
- package/dist/components/stat/object.js +1 -0
- package/dist/components/stat.css +2 -0
- package/dist/components/stat.css.d.ts +1 -0
- package/dist/components/status/index.js +7 -0
- package/dist/components/status/object.js +1 -0
- package/dist/components/status.css +2 -0
- package/dist/components/status.css.d.ts +1 -0
- package/dist/components/steps/index.js +7 -0
- package/dist/components/steps/object.js +1 -0
- package/dist/components/steps.css +2 -0
- package/dist/components/steps.css.d.ts +1 -0
- package/dist/components/surface/index.js +7 -0
- package/dist/components/surface/object.js +1 -0
- package/dist/components/surface.css +2 -0
- package/dist/components/surface.css.d.ts +1 -0
- package/dist/components/swap/index.js +7 -0
- package/dist/components/swap/object.js +1 -0
- package/dist/components/swap.css +2 -0
- package/dist/components/swap.css.d.ts +1 -0
- package/dist/components/table/index.js +7 -0
- package/dist/components/table/object.js +1 -0
- package/dist/components/table.css +2 -0
- package/dist/components/table.css.d.ts +1 -0
- package/dist/components/tabs/index.js +7 -0
- package/dist/components/tabs/object.js +1 -0
- package/dist/components/tabs.css +2 -0
- package/dist/components/tabs.css.d.ts +1 -0
- package/dist/components/tag/index.js +7 -0
- package/dist/components/tag/object.js +1 -0
- package/dist/components/tag.css +2 -0
- package/dist/components/tag.css.d.ts +1 -0
- package/dist/components/textarea/index.js +7 -0
- package/dist/components/textarea/object.js +1 -0
- package/dist/components/textarea.css +2 -0
- package/dist/components/textarea.css.d.ts +1 -0
- package/dist/components/timeline/index.js +7 -0
- package/dist/components/timeline/object.js +1 -0
- package/dist/components/timeline.css +2 -0
- package/dist/components/timeline.css.d.ts +1 -0
- package/dist/components/toast/index.js +7 -0
- package/dist/components/toast/object.js +1 -0
- package/dist/components/toast.css +2 -0
- package/dist/components/toast.css.d.ts +1 -0
- package/dist/components/toggle/index.js +7 -0
- package/dist/components/toggle/object.js +1 -0
- package/dist/components/toggle.css +2 -0
- package/dist/components/toggle.css.d.ts +1 -0
- package/dist/components/tooltip/index.js +7 -0
- package/dist/components/tooltip/object.js +1 -0
- package/dist/components/tooltip.css +2 -0
- package/dist/components/tooltip.css.d.ts +1 -0
- package/dist/frutjam.components.css +62 -0
- package/dist/frutjam.components.css.d.ts +1 -0
- package/dist/frutjam.min.css +65 -3
- package/dist/frutjam.min.css.d.ts +1 -0
- package/dist/functions/addPrefix.js +14 -0
- package/dist/plugin.js +207 -0
- package/dist/themes/darkberry/index.js +5 -0
- package/dist/themes/darkberry/object.js +1 -0
- package/dist/themes/darkberry.css +2 -0
- package/dist/themes/darkberry.css.d.ts +1 -0
- package/dist/themes/snowberry/index.js +5 -0
- package/dist/themes/snowberry/object.js +1 -0
- package/dist/themes/snowberry.css +2 -0
- package/dist/themes/snowberry.css.d.ts +1 -0
- package/dist/unocss/map.js +5 -0
- package/dist/utilities/headings/index.js +7 -0
- package/dist/utilities/headings/object.js +1 -0
- package/dist/utilities/headings.css +2 -0
- package/dist/utilities/headings.css.d.ts +1 -0
- package/dist/utilities/para/index.js +7 -0
- package/dist/utilities/para/object.js +1 -0
- package/dist/utilities/para.css +2 -0
- package/dist/utilities/para.css.d.ts +1 -0
- package/dist/utilities/typography/index.js +7 -0
- package/dist/utilities/typography/object.js +1 -0
- package/dist/utilities/typography.css +2 -0
- package/dist/utilities/typography.css.d.ts +1 -0
- package/dist/utilities/wrapper/index.js +7 -0
- package/dist/utilities/wrapper/object.js +1 -0
- package/dist/utilities/wrapper.css +2 -0
- package/dist/utilities/wrapper.css.d.ts +1 -0
- package/index.js +332 -0
- package/package.json +90 -43
- package/packages/js/carousel.js +20 -0
- package/packages/js/collapsible.js +14 -0
- package/packages/js/combobox.js +101 -0
- package/packages/js/drawer.js +1 -0
- package/packages/js/index.js +8 -0
- package/packages/js/modal.js +8 -0
- package/packages/js/tabs.js +89 -0
- package/packages/js/toast.js +27 -0
- package/packages/js/tooltip.js +8 -0
- package/packages/react/index.js +8 -0
- package/packages/react/useCarousel.js +23 -0
- package/packages/react/useCollapsible.js +20 -0
- package/packages/react/useCombobox.js +81 -0
- package/packages/react/useDrawer.js +1 -0
- package/packages/react/useModal.js +12 -0
- package/packages/react/useTabs.js +53 -0
- package/packages/react/useToast.js +27 -0
- package/packages/react/useTooltip.js +12 -0
- package/packages/unocss/index.js +77 -0
- package/dist/frutjam.dev.css +0 -2
- package/dist/frutjam.min.js +0 -2
- package/dist/frutjam.min.js.LICENSE.txt +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default {".para-2xl":{"text-wrap-style":"pretty","font-size":"var(--text-2xl)","line-height":1.6},".para-3xl":{"text-wrap-style":"pretty","font-size":"var(--text-3xl)","line-height":1.6},".para-4xl":{"text-wrap-style":"pretty","font-size":"var(--text-4xl)","line-height":1.6},".para-5xl":{"text-wrap-style":"pretty","font-size":"var(--text-5xl)","line-height":1.6},".para-6xl":{"text-wrap-style":"pretty","font-size":"var(--text-6xl)","line-height":1.6},".para-7xl":{"text-wrap-style":"pretty","font-size":"var(--text-7xl)","line-height":1.6},".para-8xl":{"text-wrap-style":"pretty","font-size":"var(--text-8xl)","line-height":1.6},".para-9xl":{"text-wrap-style":"pretty","font-size":"var(--text-9xl)","line-height":1.6},".para-lg":{"text-wrap-style":"pretty","font-size":"var(--text-lg)","line-height":1.6},".para-md":{"text-wrap-style":"pretty","line-height":1.6,"font-size":"var(--text-base)"},".para-sm":{"text-wrap-style":"pretty","font-size":"var(--text-sm)","line-height":1.6},".para-xl":{"text-wrap-style":"pretty","font-size":"var(--text-xl)","line-height":1.6},".para-xs":{"text-wrap-style":"pretty","font-size":"var(--text-xs)","line-height":1.6},".para":{"text-wrap-style":"pretty","font-size":"var(--text-base)","line-height":1.6}};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! frutjam v2.3.0 (c) 2026 Nezanuha | Released under the MIT License | https://frutjam.com */
|
|
2
|
+
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */.para-2xl{font-size:var(--text-base);font-size:var(--text-2xl)}.para,.para-2xl,.para-3xl,.para-4xl,.para-5xl,.para-6xl,.para-7xl,.para-8xl,.para-9xl,.para-lg,.para-md,.para-sm,.para-xl,.para-xs{text-wrap-style:pretty;line-height:1.6}.para-3xl{font-size:var(--text-base);font-size:var(--text-3xl)}.para-4xl{font-size:var(--text-base);font-size:var(--text-4xl)}.para-5xl{font-size:var(--text-base);font-size:var(--text-5xl)}.para-6xl{font-size:var(--text-base);font-size:var(--text-6xl)}.para-7xl{font-size:var(--text-base);font-size:var(--text-7xl)}.para-8xl{font-size:var(--text-base);font-size:var(--text-8xl)}.para-9xl{font-size:var(--text-base);font-size:var(--text-9xl)}.para-lg{font-size:var(--text-base);font-size:var(--text-lg)}.para-md,.para-sm{font-size:var(--text-base)}.para-sm{font-size:var(--text-sm)}.para-xl{font-size:var(--text-base);font-size:var(--text-xl)}.para-xs{font-size:var(--text-base);font-size:var(--text-xs)}.para{font-size:var(--text-base)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default {".prose-frutjam":{"--tw-prose-body":"var(--color-body)","--tw-prose-headings":"var(--color-on-base)","--tw-prose-lead":"var(--color-on-neutral)","--tw-prose-bold":"var(--color-on-base)","--tw-prose-links":"var(--color-primary)","--tw-prose-counters":"var(--color-neutral)","--tw-prose-bullets":"var(--color-neutral)","--tw-prose-hr":"var(--color-base-soft)","--tw-prose-quotes":"var(--color-on-base)","--tw-prose-quote-borders":"var(--color-primary)","--tw-prose-code":"var(--color-on-base)","--tw-prose-pre-code":"var(--color-on-base)","--tw-prose-pre-bg":"var(--color-base-soft)","--tw-prose-th-borders":"var(--color-base-soft)","--tw-prose-td-borders":"var(--color-base-soft)","--tw-prose-captions":"var(--color-on-neutral)","--tw-prose-kbd":"var(--color-body)","--tw-prose-invert-body":"var(--color-on-neutral)","--tw-prose-invert-headings":"var(--color-on-base)","--tw-prose-invert-links":"var(--color-primary)"}};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! frutjam v2.3.0 (c) 2026 Nezanuha | Released under the MIT License | https://frutjam.com */
|
|
2
|
+
.prose-frutjam{--tw-prose-body:var(--color-body);--tw-prose-headings:var(--color-on-base);--tw-prose-lead:var(--color-on-neutral);--tw-prose-bold:var(--color-on-base);--tw-prose-links:var(--color-primary);--tw-prose-counters:var(--color-neutral);--tw-prose-bullets:var(--color-neutral);--tw-prose-hr:var(--color-base-soft);--tw-prose-quotes:var(--color-on-base);--tw-prose-quote-borders:var(--color-primary);--tw-prose-code:var(--color-on-base);--tw-prose-pre-code:var(--color-on-base);--tw-prose-pre-bg:var(--color-base-soft);--tw-prose-th-borders:var(--color-base-soft);--tw-prose-td-borders:var(--color-base-soft);--tw-prose-captions:var(--color-on-neutral);--tw-prose-kbd:var(--color-body);--tw-prose-invert-body:var(--color-on-neutral);--tw-prose-invert-headings:var(--color-on-base);--tw-prose-invert-links:var(--color-primary)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default {".wrapper-full":{"width":"100%","max-width":"100%","box-sizing":"border-box","margin-inline":"auto","padding-inline":"1rem","@media (width >= 40rem)":{"padding-inline":"2rem"},"@media (width >= 64rem)":{"padding-inline":"4rem"}},".wrapper-2xl":{"box-sizing":"border-box","max-width":"1536px","margin-inline":"auto","padding-inline":"1rem","@media (width >= 40rem)":{"padding-inline":"2rem"},"@media (width >= 64rem)":{"padding-inline":"4rem"}},".wrapper-lg":{"box-sizing":"border-box","max-width":"1024px","margin-inline":"auto","padding-inline":"1rem","@media (width >= 40rem)":{"padding-inline":"2rem"},"@media (width >= 64rem)":{"padding-inline":"4rem"}},".wrapper-md":{"box-sizing":"border-box","max-width":"768px","margin-inline":"auto","padding-inline":"1rem","@media (width >= 40rem)":{"padding-inline":"2rem"},"@media (width >= 64rem)":{"padding-inline":"4rem"}},".wrapper-sm":{"box-sizing":"border-box","max-width":"640px","margin-inline":"auto","padding-inline":"1rem","@media (width >= 40rem)":{"padding-inline":"2rem"},"@media (width >= 64rem)":{"padding-inline":"4rem"}},".wrapper-xl":{"box-sizing":"border-box","max-width":"1280px","margin-inline":"auto","padding-inline":"1rem","@media (width >= 40rem)":{"padding-inline":"2rem"},"@media (width >= 64rem)":{"padding-inline":"4rem"}},".wrapper-xs":{"box-sizing":"border-box","max-width":"320px","margin-inline":"auto","padding-inline":"1rem","@media (width >= 40rem)":{"padding-inline":"2rem"},"@media (width >= 64rem)":{"padding-inline":"4rem"}},".wrapper":{"box-sizing":"border-box","margin-inline":"auto","padding-inline":"1rem","@media (width >= 40rem)":{"padding-inline":"2rem"},"@media (width >= 64rem)":{"padding-inline":"4rem"}}};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! frutjam v2.3.0 (c) 2026 Nezanuha | Released under the MIT License | https://frutjam.com */
|
|
2
|
+
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */.wrapper-full{width:100%;max-width:100%;box-sizing:border-box;margin-inline:auto;padding-inline:1rem;@media(width>=40rem){padding-inline:2rem}@media(width>=64rem){padding-inline:4rem}}.wrapper-2xl{box-sizing:border-box;max-width:1536px;margin-inline:auto;padding-inline:1rem;@media(width>=40rem){padding-inline:2rem}@media(width>=64rem){padding-inline:4rem}}.wrapper-lg{box-sizing:border-box;max-width:1024px;margin-inline:auto;padding-inline:1rem;@media(width>=40rem){padding-inline:2rem}@media(width>=64rem){padding-inline:4rem}}.wrapper-md{box-sizing:border-box;max-width:768px;margin-inline:auto;padding-inline:1rem;@media(width>=40rem){padding-inline:2rem}@media(width>=64rem){padding-inline:4rem}}.wrapper-sm{box-sizing:border-box;max-width:640px;margin-inline:auto;padding-inline:1rem;@media(width>=40rem){padding-inline:2rem}@media(width>=64rem){padding-inline:4rem}}.wrapper-xl{box-sizing:border-box;max-width:1280px;margin-inline:auto;padding-inline:1rem;@media(width>=40rem){padding-inline:2rem}@media(width>=64rem){padding-inline:4rem}}.wrapper-xs{box-sizing:border-box;max-width:320px;margin-inline:auto;padding-inline:1rem;@media(width>=40rem){padding-inline:2rem}@media(width>=64rem){padding-inline:4rem}}.wrapper{box-sizing:border-box;margin-inline:auto;padding-inline:1rem;@media(width>=40rem){padding-inline:2rem}@media(width>=64rem){padding-inline:4rem}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/index.js
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { readFileSync, readdirSync, statSync, existsSync } from "fs"
|
|
2
|
+
import { join, dirname, basename, extname } from "path"
|
|
3
|
+
import { fileURLToPath } from "url"
|
|
4
|
+
import postcss from "postcss"
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
7
|
+
const __dir = dirname(__filename)
|
|
8
|
+
const srcDir = join(__dir, "src")
|
|
9
|
+
const VERSION = JSON.parse(readFileSync(join(__dir, "package.json"), "utf8")).version
|
|
10
|
+
|
|
11
|
+
// ── Keys that must be injected after Tailwind processes layers ───────────────
|
|
12
|
+
// These files conflict with @layer utilities from Tailwind plugins (e.g. typography),
|
|
13
|
+
// so they are injected via OnceExit into @layer utilities (or root if not found).
|
|
14
|
+
const LATE_INJECT = new Set(['typography'])
|
|
15
|
+
|
|
16
|
+
// ── Registry (built once at startup, not per build) ──────────────────────────
|
|
17
|
+
|
|
18
|
+
function buildRegistry() {
|
|
19
|
+
const registry = {}
|
|
20
|
+
|
|
21
|
+
// Components: src/components/*/base.css — keyed by folder name
|
|
22
|
+
const componentsDir = join(srcDir, "components")
|
|
23
|
+
for (const name of readdirSync(componentsDir)) {
|
|
24
|
+
const entry = join(componentsDir, name, "base.css")
|
|
25
|
+
if (statSync(join(componentsDir, name)).isDirectory() && existsSync(entry)) {
|
|
26
|
+
registry[name] = entry
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Utilities: src/utilities/*.css — keyed by filename without extension
|
|
31
|
+
const utilitiesDir = join(srcDir, "utilities")
|
|
32
|
+
for (const file of readdirSync(utilitiesDir)) {
|
|
33
|
+
if (extname(file) === ".css") {
|
|
34
|
+
registry[basename(file, ".css")] = join(utilitiesDir, file)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return registry
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const REGISTRY = buildRegistry()
|
|
42
|
+
|
|
43
|
+
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
function readFile(p) { return readFileSync(p, "utf8") }
|
|
46
|
+
|
|
47
|
+
function parseList(val) {
|
|
48
|
+
if (!val) return []
|
|
49
|
+
if (Array.isArray(val)) return val.map(s => s.trim()).filter(Boolean)
|
|
50
|
+
return String(val).split(",").map(s => s.trim()).filter(Boolean)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function applyPrefix(css, prefix) {
|
|
54
|
+
if (!prefix) return css
|
|
55
|
+
|
|
56
|
+
css = css.replace(
|
|
57
|
+
/@utility\s+([\w-]+\*?)\s*\{/g,
|
|
58
|
+
(_, name) => `@utility ${prefix}-${name} {`
|
|
59
|
+
)
|
|
60
|
+
css = css.replace(
|
|
61
|
+
/@apply\s+([^;{}\n]+)/g,
|
|
62
|
+
(_, classes) => `@apply ${classes.trim().split(/\s+/).map(c => `${prefix}-${c}`).join(' ')}`
|
|
63
|
+
)
|
|
64
|
+
css = css.replace(
|
|
65
|
+
/\/\*[\s\S]*?\*\/|"[^"]*"|'[^']*'|\.(([a-z][a-z0-9]*-?)+)/g,
|
|
66
|
+
(match, name) => name ? `.${prefix}-${name}` : match
|
|
67
|
+
)
|
|
68
|
+
return css
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function buildUtilityMap(css) {
|
|
72
|
+
const map = new Map()
|
|
73
|
+
const pattern = /@utility\s+([\w-]+(?:\s*\*)?)\s*\{/g
|
|
74
|
+
let match
|
|
75
|
+
while ((match = pattern.exec(css)) !== null) {
|
|
76
|
+
const name = match[1].trim()
|
|
77
|
+
if (map.has(name)) continue
|
|
78
|
+
let depth = 0, bodyStart = -1
|
|
79
|
+
for (let i = match.index + match[0].length - 1; i < css.length; i++) {
|
|
80
|
+
if (css[i] === '{') {
|
|
81
|
+
if (depth === 0) bodyStart = i + 1
|
|
82
|
+
depth++
|
|
83
|
+
} else if (css[i] === '}') {
|
|
84
|
+
if (--depth === 0) { map.set(name, css.slice(bodyStart, i)); break }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return map
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function resolveCopy(css, source = css) {
|
|
92
|
+
const map = source instanceof Map ? source : buildUtilityMap(source)
|
|
93
|
+
return css.replace(/@copy\s+([\w-]+)\s*;/g, (original, name) => map.get(name) ?? original)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function resolveImports(css, baseDir) {
|
|
97
|
+
return css.replace(
|
|
98
|
+
/@import\s+["']([^"']+)["'];?\s*/g,
|
|
99
|
+
(_, importPath) => {
|
|
100
|
+
const fullPath = join(baseDir, importPath)
|
|
101
|
+
const importedDir = dirname(fullPath)
|
|
102
|
+
const importedCSS = readFile(fullPath)
|
|
103
|
+
return resolveImports(importedCSS, importedDir)
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function buildCustomThemes(themes) {
|
|
109
|
+
if (!themes || Object.keys(themes).length === 0) return ""
|
|
110
|
+
|
|
111
|
+
return Object.entries(themes)
|
|
112
|
+
.map(([name, vars]) => {
|
|
113
|
+
const varString = Object.entries(vars)
|
|
114
|
+
.map(([k, v]) => ` ${k}: ${v};`)
|
|
115
|
+
.join("\n")
|
|
116
|
+
return `[data-theme="${name}"] {\n${varString}\n}`
|
|
117
|
+
})
|
|
118
|
+
.join("\n\n")
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ── Base layers (inlined — no entry CSS files needed) ───────────────────────
|
|
122
|
+
|
|
123
|
+
const BASE_FULL = [
|
|
124
|
+
"base/reboot.css",
|
|
125
|
+
"theme/base.css",
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
const BASE_CORE = [
|
|
129
|
+
"base/variants.css",
|
|
130
|
+
"base/tokens.css",
|
|
131
|
+
"theme/base.css",
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
// ── Build ────────────────────────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
// Scope one selector part (already split from a comma list) to rootSelector.
|
|
137
|
+
// Returns an array of replacement selectors.
|
|
138
|
+
// [data-theme] selectors become compound with rootSelector so theme variables
|
|
139
|
+
// only apply when data-theme is set directly on the root element — no ancestor
|
|
140
|
+
// inheritance, which would break the isolation guarantee of the root option.
|
|
141
|
+
function scopeThemeSelector(sel, rootSelector) {
|
|
142
|
+
const t = sel.trimStart()
|
|
143
|
+
const lead = sel.slice(0, sel.length - t.length)
|
|
144
|
+
|
|
145
|
+
// Already remapped from :root (e.g. rootSelector:not([data-theme])) — keep as-is
|
|
146
|
+
if (t.startsWith(rootSelector)) return [sel]
|
|
147
|
+
|
|
148
|
+
// :is([data-theme="x"], [data-theme="y"]) — flatten into individual compound selectors
|
|
149
|
+
if (t.startsWith(":is([data-theme")) {
|
|
150
|
+
const values = []
|
|
151
|
+
const re = /\[data-theme=["']([^"']+)["']\]/g
|
|
152
|
+
let m
|
|
153
|
+
while ((m = re.exec(t)) !== null) values.push(m[1])
|
|
154
|
+
return values.map(v => `${lead}${rootSelector}[data-theme="${v}"]`)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// [data-theme="x"] — compound with rootSelector
|
|
158
|
+
if (/^\[data-theme=/.test(t)) {
|
|
159
|
+
const m = t.match(/^\[data-theme=["']([^"']+)["']\]/)
|
|
160
|
+
if (m) return [`${lead}${rootSelector}[data-theme="${m[1]}"]`]
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// [data-theme] without value (base tokens) — compound with rootSelector
|
|
164
|
+
if (t.startsWith("[data-theme]")) {
|
|
165
|
+
return [`${lead}${rootSelector}[data-theme]`]
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return [sel]
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function buildCSS(prefix, themes, reset, rootSelector, include, exclude) {
|
|
172
|
+
// Resolve which components+utilities to include
|
|
173
|
+
let keys = Object.keys(REGISTRY)
|
|
174
|
+
if (include.length > 0) {
|
|
175
|
+
keys = include.filter(k => REGISTRY[k])
|
|
176
|
+
} else if (exclude.length > 0) {
|
|
177
|
+
const excludeSet = new Set(exclude)
|
|
178
|
+
keys = keys.filter(k => !excludeSet.has(k))
|
|
179
|
+
}
|
|
180
|
+
// LATE_INJECT keys are handled separately via OnceExit — skip them here
|
|
181
|
+
keys = keys.filter(k => !LATE_INJECT.has(k))
|
|
182
|
+
|
|
183
|
+
// Base layer (full with reboot, or core without)
|
|
184
|
+
const baseFiles = reset ? BASE_FULL : BASE_CORE
|
|
185
|
+
const baseCss = baseFiles
|
|
186
|
+
.map(f => resolveImports(readFile(join(srcDir, f)), join(srcDir, dirname(f))))
|
|
187
|
+
.join("\n")
|
|
188
|
+
|
|
189
|
+
// Components + utilities
|
|
190
|
+
const modulesCss = keys
|
|
191
|
+
.map(k => resolveImports(readFile(REGISTRY[k]), dirname(REGISTRY[k])))
|
|
192
|
+
.join("\n")
|
|
193
|
+
|
|
194
|
+
const combined = baseCss + "\n" + modulesCss
|
|
195
|
+
const utilMap = buildUtilityMap(combined)
|
|
196
|
+
|
|
197
|
+
// Base CSS (theme variables, token definitions, text-color overrides) must NOT be prefixed —
|
|
198
|
+
// the @utility text-* declarations target the same class names as Tailwind's theme-generated
|
|
199
|
+
// utilities and must stay unprefixed (e.g. text-primary, not fj-text-primary).
|
|
200
|
+
let resolvedBase = resolveCopy(baseCss, utilMap)
|
|
201
|
+
let resolvedModules = resolveCopy(modulesCss, utilMap)
|
|
202
|
+
|
|
203
|
+
if (rootSelector !== ":root") {
|
|
204
|
+
resolvedBase = resolvedBase.replace(/:root\b/g, rootSelector)
|
|
205
|
+
resolvedModules = resolvedModules.replace(/:root\b/g, rootSelector)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
let css = resolvedBase + "\n" + applyPrefix(resolvedModules, prefix)
|
|
209
|
+
|
|
210
|
+
const customThemes = buildCustomThemes(themes)
|
|
211
|
+
if (customThemes) css += "\n\n" + customThemes
|
|
212
|
+
|
|
213
|
+
if (rootSelector !== ":root") {
|
|
214
|
+
const ast = postcss.parse(css)
|
|
215
|
+
ast.walkRules(rule => {
|
|
216
|
+
rule.selectors = rule.selectors.flatMap(sel => scopeThemeSelector(sel, rootSelector))
|
|
217
|
+
})
|
|
218
|
+
css = ast.toString()
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Extract @utility text-* rules from base CSS and convert to plain CSS for late injection.
|
|
222
|
+
// In the PostCSS path, Tailwind's theme-generated text-{color} utilities take precedence over
|
|
223
|
+
// same-named @utility definitions. Injecting them as plain CSS at the end of @layer utilities
|
|
224
|
+
// via OnceExit ensures they appear after the theme-generated rules and therefore win.
|
|
225
|
+
const baseUtilMap = buildUtilityMap(resolvedBase)
|
|
226
|
+
const textColorOverrides = [...baseUtilMap.entries()]
|
|
227
|
+
.filter(([name]) => name.startsWith('text-'))
|
|
228
|
+
.map(([name, body]) => `.${name} {${body}}`)
|
|
229
|
+
.join('\n')
|
|
230
|
+
|
|
231
|
+
return { css, utilMap, textColorOverrides }
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function buildLateCSS(prefix, include, exclude, utilMap = new Map()) {
|
|
235
|
+
let keys = [...LATE_INJECT].filter(k => REGISTRY[k])
|
|
236
|
+
if (include.length > 0) {
|
|
237
|
+
keys = keys.filter(k => include.includes(k))
|
|
238
|
+
} else if (exclude.length > 0) {
|
|
239
|
+
const excludeSet = new Set(exclude)
|
|
240
|
+
keys = keys.filter(k => !excludeSet.has(k))
|
|
241
|
+
}
|
|
242
|
+
if (keys.length === 0) return ""
|
|
243
|
+
const raw = keys
|
|
244
|
+
.map(k => resolveImports(readFile(REGISTRY[k]), dirname(REGISTRY[k])))
|
|
245
|
+
.join("\n")
|
|
246
|
+
const css = resolveCopy(raw, utilMap)
|
|
247
|
+
return applyPrefix(css, prefix)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ── Plugin ───────────────────────────────────────────────────────────────────
|
|
251
|
+
|
|
252
|
+
function parseAtPluginOptions(atRule) {
|
|
253
|
+
const opts = {}
|
|
254
|
+
atRule.each(node => {
|
|
255
|
+
if (node.type !== "decl") return
|
|
256
|
+
let val = node.value.trim()
|
|
257
|
+
if (val === "true") val = true
|
|
258
|
+
else if (val === "false") val = false
|
|
259
|
+
else if (
|
|
260
|
+
(val.startsWith('"') && val.endsWith('"')) ||
|
|
261
|
+
(val.startsWith("'") && val.endsWith("'"))
|
|
262
|
+
) val = val.slice(1, -1)
|
|
263
|
+
opts[node.prop.trim()] = val
|
|
264
|
+
})
|
|
265
|
+
return opts
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export { buildUtilityMap, resolveCopy }
|
|
269
|
+
|
|
270
|
+
export default function frutjam(options = {}) {
|
|
271
|
+
let _lateCss = ""
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
postcssPlugin: "frutjam",
|
|
275
|
+
Once(root) {
|
|
276
|
+
// Collect @plugin "frutjam" { ... } blocks from the CSS
|
|
277
|
+
const atPluginNodes = []
|
|
278
|
+
root.each(node => {
|
|
279
|
+
if (
|
|
280
|
+
node.type === "atrule" &&
|
|
281
|
+
node.name === "plugin" &&
|
|
282
|
+
/^["']frutjam["']/.test(node.params.trim()) &&
|
|
283
|
+
node.nodes
|
|
284
|
+
) atPluginNodes.push(node)
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
// CSS-level options override factory options
|
|
288
|
+
let cssOptions = {}
|
|
289
|
+
for (const node of atPluginNodes) {
|
|
290
|
+
Object.assign(cssOptions, parseAtPluginOptions(node))
|
|
291
|
+
node.remove()
|
|
292
|
+
}
|
|
293
|
+
const merged = { ...options, ...cssOptions }
|
|
294
|
+
|
|
295
|
+
const prefix = typeof merged.prefix === "string" ? merged.prefix.trim() : ""
|
|
296
|
+
const themes = typeof merged.themes === "object" ? merged.themes : {}
|
|
297
|
+
const reset = merged.reset !== false
|
|
298
|
+
const rootSelector = typeof merged.root === "string" ? merged.root : ":root"
|
|
299
|
+
const logs = merged.logs !== false
|
|
300
|
+
const include = parseList(merged.include)
|
|
301
|
+
const exclude = parseList(merged.exclude)
|
|
302
|
+
|
|
303
|
+
const { css, utilMap, textColorOverrides } = buildCSS(prefix, themes, reset, rootSelector, include, exclude)
|
|
304
|
+
root.append(postcss.parse(css).nodes)
|
|
305
|
+
_lateCss = buildLateCSS(prefix, include, exclude, utilMap)
|
|
306
|
+
if (textColorOverrides) _lateCss += '\n' + textColorOverrides
|
|
307
|
+
if (logs) {
|
|
308
|
+
const d = "\x1b[2m", r = "\x1b[0m"
|
|
309
|
+
const badge = "\x1b[46m\x1b[30m frutjam \x1b[0m"
|
|
310
|
+
console.log("\n" + badge + " " + d + "v" + VERSION + r + "\n")
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
OnceExit(root) {
|
|
314
|
+
if (!_lateCss) return
|
|
315
|
+
// Inject into @layer utilities if it exists (correct cascade layer),
|
|
316
|
+
// otherwise append to root (unlayered CSS beats all @layer rules).
|
|
317
|
+
let utilLayer = null
|
|
318
|
+
root.each(node => {
|
|
319
|
+
if (node.type === "atrule" && node.name === "layer" && node.params === "utilities" && node.nodes) {
|
|
320
|
+
utilLayer = node
|
|
321
|
+
}
|
|
322
|
+
})
|
|
323
|
+
const nodes = postcss.parse(_lateCss).nodes
|
|
324
|
+
if (utilLayer) {
|
|
325
|
+
utilLayer.append(nodes)
|
|
326
|
+
} else {
|
|
327
|
+
root.append(nodes)
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
frutjam.postcss = true
|
package/package.json
CHANGED
|
@@ -1,41 +1,80 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frutjam",
|
|
3
|
-
"version": "2.3.0
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
"version": "2.3.0",
|
|
4
|
+
"description": "CSS-only Tailwind CSS v4 component library, also usable with UnoCSS or no build step at all. 65+ accessible UI components, zero JavaScript, WCAG AA guaranteed. Free MCP server for AI editors included.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./dist/plugin.js",
|
|
8
|
+
"./unocss": "./packages/unocss/index.js",
|
|
9
|
+
"./css": {
|
|
10
|
+
"types": "./dist/frutjam.components.css.d.ts",
|
|
11
|
+
"default": "./dist/frutjam.components.css"
|
|
12
|
+
},
|
|
13
|
+
"./css/full": {
|
|
14
|
+
"types": "./dist/frutjam.min.css.d.ts",
|
|
15
|
+
"default": "./dist/frutjam.min.css"
|
|
16
|
+
},
|
|
17
|
+
"./css/base": {
|
|
18
|
+
"types": "./dist/base.css.d.ts",
|
|
19
|
+
"default": "./dist/base.css"
|
|
20
|
+
},
|
|
21
|
+
"./css/components/*": {
|
|
22
|
+
"types": "./dist/components/*.css.d.ts",
|
|
23
|
+
"default": "./dist/components/*.css"
|
|
24
|
+
},
|
|
25
|
+
"./css/utilities/*": {
|
|
26
|
+
"types": "./dist/utilities/*.css.d.ts",
|
|
27
|
+
"default": "./dist/utilities/*.css"
|
|
28
|
+
},
|
|
29
|
+
"./css/themes/*": {
|
|
30
|
+
"types": "./dist/themes/*.css.d.ts",
|
|
31
|
+
"default": "./dist/themes/*.css"
|
|
32
|
+
},
|
|
33
|
+
"./postcss": "./index.js",
|
|
34
|
+
"./js": "./packages/js/index.js",
|
|
35
|
+
"./js/*": "./packages/js/*.js",
|
|
36
|
+
"./react": "./packages/react/index.js",
|
|
37
|
+
"./react/*": "./packages/react/*.js",
|
|
38
|
+
"./components/*": "./dist/components/*/index.js",
|
|
39
|
+
"./utilities/*": "./dist/utilities/*/index.js",
|
|
40
|
+
"./themes/*": "./dist/themes/*/index.js",
|
|
41
|
+
"./base/tokens": "./dist/base/tokens/index.js",
|
|
42
|
+
"./base/reboot": "./dist/base/reboot/index.js",
|
|
43
|
+
"./functions/addPrefix": "./dist/functions/addPrefix.js"
|
|
44
|
+
},
|
|
9
45
|
"files": [
|
|
46
|
+
"index.js",
|
|
10
47
|
"dist/",
|
|
11
|
-
"
|
|
12
|
-
"LICENSE"
|
|
48
|
+
"packages/"
|
|
13
49
|
],
|
|
50
|
+
"style": "./dist/frutjam.min.css",
|
|
51
|
+
"unpkg": "./dist/frutjam.min.css",
|
|
52
|
+
"jsdelivr": "./dist/frutjam.min.css",
|
|
53
|
+
"scripts": {
|
|
54
|
+
"clean": "rimraf ./dist",
|
|
55
|
+
"build:cdn": "node scripts/build-cdn.mjs",
|
|
56
|
+
"build:plugin": "node scripts/build-plugin.mjs",
|
|
57
|
+
"build": "rimraf ./dist && node scripts/build-cdn.mjs && node scripts/build-plugin.mjs && node scripts/build-unocss.mjs",
|
|
58
|
+
"prepublishOnly": "rimraf ./dist && node scripts/build-cdn.mjs && node scripts/build-plugin.mjs && node scripts/build-unocss.mjs && node -e \"const fs=require('fs');fs.copyFileSync('README.md','README.github.md');fs.copyFileSync('README.npm.md','README.md');\"",
|
|
59
|
+
"postpublish": "node -e \"const fs=require('fs');fs.copyFileSync('README.github.md','README.md');fs.unlinkSync('README.github.md');\"",
|
|
60
|
+
"postversion": "git push origin main --tags",
|
|
61
|
+
"build:unocss": "node scripts/build-unocss.mjs"
|
|
62
|
+
},
|
|
63
|
+
"funding": "https://ko-fi.com/nezanuha",
|
|
14
64
|
"repository": {
|
|
15
65
|
"type": "git",
|
|
16
66
|
"url": "git+https://github.com/nezanuha/frutjam.git"
|
|
17
67
|
},
|
|
18
|
-
"exports": {
|
|
19
|
-
".": "./dist/frutjam.dev.css"
|
|
20
|
-
},
|
|
21
|
-
"scripts": {
|
|
22
|
-
"clean": "rimraf ./dist",
|
|
23
|
-
"dev": "webpack serve --mode=development",
|
|
24
|
-
"build:browser": "node scripts/build/browser.mjs",
|
|
25
|
-
"build:local": "webpack --config scripts/build/local.js --mode=production",
|
|
26
|
-
"build": "npm run clean && npm run build:browser && npm run build:local",
|
|
27
|
-
"prepublishOnly": "npm run build"
|
|
28
|
-
},
|
|
29
68
|
"publishConfig": {
|
|
30
|
-
"
|
|
31
|
-
"
|
|
69
|
+
"access": "public",
|
|
70
|
+
"provenance": true
|
|
32
71
|
},
|
|
33
72
|
"keywords": [
|
|
34
73
|
"frutjam",
|
|
35
74
|
"tailwindcss",
|
|
75
|
+
"tailwind css plugin",
|
|
36
76
|
"UI Library",
|
|
37
77
|
"tailwind",
|
|
38
|
-
"taiwind css",
|
|
39
78
|
"components",
|
|
40
79
|
"tailwind components",
|
|
41
80
|
"tailwind component library",
|
|
@@ -48,18 +87,20 @@
|
|
|
48
87
|
"tailwind django",
|
|
49
88
|
"tailwind rails",
|
|
50
89
|
"tailwind laravel",
|
|
51
|
-
"tailwind css plugin",
|
|
52
90
|
"component library",
|
|
53
91
|
"tailwind themes",
|
|
54
92
|
"css library",
|
|
55
93
|
"theme",
|
|
56
|
-
"
|
|
57
|
-
"framework",
|
|
58
|
-
"W3C standards",
|
|
94
|
+
"design system",
|
|
59
95
|
"accessibility web development",
|
|
60
96
|
"seo friendly ui",
|
|
61
97
|
"rtl ui",
|
|
62
|
-
"responsive ui library"
|
|
98
|
+
"responsive ui library",
|
|
99
|
+
"unocss",
|
|
100
|
+
"unocss preset",
|
|
101
|
+
"unocss component library",
|
|
102
|
+
"css components",
|
|
103
|
+
"vanilla css"
|
|
63
104
|
],
|
|
64
105
|
"author": "Nezanuha",
|
|
65
106
|
"license": "MIT",
|
|
@@ -67,23 +108,29 @@
|
|
|
67
108
|
"bugs": {
|
|
68
109
|
"url": "https://github.com/nezanuha/frutjam/issues"
|
|
69
110
|
},
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
|
|
111
|
+
"engines": {
|
|
112
|
+
"node": ">=18"
|
|
113
|
+
},
|
|
114
|
+
"peerDependencies": {
|
|
115
|
+
"tailwindcss": "^4.0.0",
|
|
116
|
+
"react": ">=18",
|
|
117
|
+
"@unocss/core": ">=66"
|
|
118
|
+
},
|
|
119
|
+
"peerDependenciesMeta": {
|
|
120
|
+
"react": {
|
|
121
|
+
"optional": true
|
|
122
|
+
},
|
|
123
|
+
"@unocss/core": {
|
|
124
|
+
"optional": true
|
|
125
|
+
}
|
|
73
126
|
},
|
|
74
127
|
"devDependencies": {
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"postcss": "^8.5.
|
|
79
|
-
"postcss-
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"rimraf": "^6.0.1",
|
|
83
|
-
"style-loader": "^4.0.0",
|
|
84
|
-
"webpack": "^5.101.0",
|
|
85
|
-
"webpack-cli": "^6.0.1",
|
|
86
|
-
"webpack-dev-server": "^5.2.2",
|
|
87
|
-
"webpack-remove-empty-scripts": "^1.1.1"
|
|
128
|
+
"@tailwindcss/postcss": "^4.3.3",
|
|
129
|
+
"autoprefixer": "^10.6.1",
|
|
130
|
+
"cssnano": "^9.3.2",
|
|
131
|
+
"postcss": "^8.5.29",
|
|
132
|
+
"postcss-js": "^5.1.0",
|
|
133
|
+
"rimraf": "^6.1.3",
|
|
134
|
+
"tailwindcss": "^4.3.3"
|
|
88
135
|
}
|
|
89
136
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function createCarousel(el) {
|
|
2
|
+
const getItems = () => Array.from(el.querySelectorAll('.carousel-item'))
|
|
3
|
+
let current = 0
|
|
4
|
+
|
|
5
|
+
function goTo(index) {
|
|
6
|
+
const items = getItems()
|
|
7
|
+
if (!items.length) return
|
|
8
|
+
current = Math.max(0, Math.min(index, items.length - 1))
|
|
9
|
+
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
10
|
+
items[current].scrollIntoView({ behavior: reduced ? 'auto' : 'smooth', block: 'nearest', inline: 'start' })
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
goTo,
|
|
15
|
+
next: () => goTo(current + 1),
|
|
16
|
+
prev: () => goTo(current - 1),
|
|
17
|
+
current: () => current,
|
|
18
|
+
count: () => getItems().length,
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function createCollapsible(el) {
|
|
2
|
+
function syncAria() {
|
|
3
|
+
const isOpen = el.classList.contains('collapsible-open')
|
|
4
|
+
const trigger = el.querySelector('[aria-expanded]')
|
|
5
|
+
if (trigger) trigger.setAttribute('aria-expanded', String(isOpen))
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
open: () => { el.classList.add('collapsible-open'); syncAria() },
|
|
10
|
+
close: () => { el.classList.remove('collapsible-open'); syncAria() },
|
|
11
|
+
toggle: () => { el.classList.toggle('collapsible-open'); syncAria() },
|
|
12
|
+
isOpen: () => el.classList.contains('collapsible-open'),
|
|
13
|
+
}
|
|
14
|
+
}
|