@plexui/styles 0.0.0-stage → 0.1.1
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 +21 -0
- package/README.md +28 -2
- package/dist/es/components/Accordion/Accordion.module.css +89 -0
- package/dist/es/components/Alert/Alert.module.css +210 -0
- package/dist/es/components/Avatar/Avatar.module.css +168 -0
- package/dist/es/components/Avatar/AvatarGroup.module.css +26 -0
- package/dist/es/components/Badge/Badge.module.css +196 -0
- package/dist/es/components/Breadcrumb/Breadcrumb.module.css +59 -0
- package/dist/es/components/Button/Button.module.css +863 -0
- package/dist/es/components/ButtonGroup/ButtonGroup.module.css +158 -0
- package/dist/es/components/Card/Card.module.css +125 -0
- package/dist/es/components/Checkbox/Checkbox.module.css +225 -0
- package/dist/es/components/CodeBlock/CodeBlock.module.css +139 -0
- package/dist/es/components/DataTable/DataTable.module.css +117 -0
- package/dist/es/components/DatePicker/Calendar.module.css +271 -0
- package/dist/es/components/DateRangePicker/Calendar.module.css +295 -0
- package/dist/es/components/DateRangePicker/Trigger.module.css +25 -0
- package/dist/es/components/Dialog/Dialog.module.css +240 -0
- package/dist/es/components/EmptyMessage/EmptyMessage.module.css +116 -0
- package/dist/es/components/Field/Field.module.css +152 -0
- package/dist/es/components/FieldError/FieldError.module.css +28 -0
- package/dist/es/components/FileUpload/FileUpload.module.css +133 -0
- package/dist/es/components/FloatingLabelInput/FloatingLabelInput.module.css +220 -0
- package/dist/es/components/FloatingLabelSelect/FloatingLabelSelect.module.css +152 -0
- package/dist/es/components/Image/Image.module.css +9 -0
- package/dist/es/components/Indicator/CircularProgress.module.css +28 -0
- package/dist/es/components/Indicator/LoadingDots.module.css +35 -0
- package/dist/es/components/Indicator/LoadingIndicator.module.css +41 -0
- package/dist/es/components/Input/Input.module.css +519 -0
- package/dist/es/components/InputGroup/InputGroup.module.css +309 -0
- package/dist/es/components/Kbd/Kbd.module.css +47 -0
- package/dist/es/components/Label/Label.module.css +16 -0
- package/dist/es/components/Markdown/Markdown.module.css +233 -0
- package/dist/es/components/MarkdownEditor/MarkdownEditor.module.css +297 -0
- package/dist/es/components/Menu/Menu.module.css +389 -0
- package/dist/es/components/OTPInput/OTPInput.module.css +70 -0
- package/dist/es/components/Popover/Popover.module.css +82 -0
- package/dist/es/components/Progress/Progress.module.css +34 -0
- package/dist/es/components/ProgressSteps/ProgressSteps.module.css +246 -0
- package/dist/es/components/RadioGroup/RadioGroup.module.css +159 -0
- package/dist/es/components/Select/Select.module.css +341 -0
- package/dist/es/components/SelectControl/SelectControl.module.css +558 -0
- package/dist/es/components/Separator/Separator.module.css +16 -0
- package/dist/es/components/ShimmerText/ShimmerText.module.css +43 -0
- package/dist/es/components/Sidebar/Sidebar.module.css +913 -0
- package/dist/es/components/Skeleton/Skeleton.module.css +17 -0
- package/dist/es/components/Slider/Slider.module.css +199 -0
- package/dist/es/components/StatCard/StatCard.module.css +234 -0
- package/dist/es/components/Switch/Switch.module.css +121 -0
- package/dist/es/components/Table/Table.module.css +56 -0
- package/dist/es/components/Tabs/Tabs.module.css +688 -0
- package/dist/es/components/TagInput/TagInput.module.css +320 -0
- package/dist/es/components/TextLink/TextLink.module.css +50 -0
- package/dist/es/components/Textarea/Textarea.module.css +272 -0
- package/dist/es/components/Toast/Toast.module.css +99 -0
- package/dist/es/components/Tooltip/Tooltip.module.css +91 -0
- package/dist/es/components/Transition/Animate.module.css +54 -0
- package/dist/es/components/Transition/AnimateLayout.module.css +101 -0
- package/dist/es/components/Transition/AnimateLayoutGroup.module.css +88 -0
- package/dist/es/components/Transition/TransitionGroup.module.css +4 -0
- package/dist/es/styles/base.css +102 -0
- package/dist/es/styles/globals.css +50 -0
- package/dist/es/styles/index.css +22 -0
- package/dist/es/styles/katex.min.css +1296 -0
- package/dist/es/styles/tailwind-utilities.css +1316 -0
- package/dist/es/styles/touch.css +71 -0
- package/dist/es/styles/variables-components.css +626 -0
- package/dist/es/styles/variables-primitive.css +327 -0
- package/dist/es/styles/variables-semantic.css +629 -0
- package/dist/es/styles/variables-tailwind-theme.css +34 -0
- package/dist/es/styles/variables-tokens-root.css +40 -0
- package/dist/es/types.js +4 -0
- package/dist/es/types.js.map +1 -0
- package/dist/es/vite/index.js +54 -0
- package/dist/es/vite/index.js.map +1 -0
- package/dist/types/types.d.ts +15 -0
- package/dist/types/vite/index.d.ts +55 -0
- package/package.json +109 -4
- package/postcss/functions.mjs +23 -0
- package/postcss/injectBreakpoints.mjs +47 -0
- package/postcss/mixins.mjs +100 -0
- package/postcss/parseLightDark.mjs +174 -0
- package/postcss/wrapModulesInLayer.mjs +21 -0
- package/postcss.config.mjs +52 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Plex UI tokens that semantic + components tiers reference via var(...).
|
|
3
|
+
*
|
|
4
|
+
* These live in a plain `:root` block (NOT in `@theme static`), so Plex UI
|
|
5
|
+
* does not reset Tailwind's theme namespaces with `--radius-*: initial`.
|
|
6
|
+
* Being outside any cascade layer, they still win over the same names in
|
|
7
|
+
* Tailwind's `theme` layer: the consumer's `rounded-*`, `font-sans` and
|
|
8
|
+
* `font-*` weight utilities resolve to the Plex values (`rounded-md` is
|
|
9
|
+
* 8px). The installation guide documents this.
|
|
10
|
+
*
|
|
11
|
+
* The standalone `@plexui/ui/styles/variables-tailwind-theme.css` file also
|
|
12
|
+
* maps the rest of the Plex scale (text sizes, breakpoints, ...) onto
|
|
13
|
+
* Tailwind's theme (see /docs/installation).
|
|
14
|
+
*/
|
|
15
|
+
:root {
|
|
16
|
+
/* Radii */
|
|
17
|
+
--radius-2xs: 0.125rem; /* 2px */
|
|
18
|
+
--radius-xs: 0.25rem; /* 4px */
|
|
19
|
+
--radius-sm: 0.375rem; /* 6px */
|
|
20
|
+
--radius-md: 0.5rem; /* 8px */
|
|
21
|
+
--radius-lg: 0.625rem; /* 10px */
|
|
22
|
+
--radius-xl: 0.75rem; /* 12px */
|
|
23
|
+
--radius-2xl: 1rem; /* 16px */
|
|
24
|
+
--radius-3xl: 1.25rem; /* 20px */
|
|
25
|
+
--radius-4xl: 1.5rem; /* 24px */
|
|
26
|
+
--radius-full: 9999px;
|
|
27
|
+
|
|
28
|
+
/* Font stacks */
|
|
29
|
+
--font-sans: ui-sans-serif, -apple-system, system-ui, "Segoe UI", "Noto Sans", "Helvetica",
|
|
30
|
+
"Arial", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", sans-serif;
|
|
31
|
+
--font-serif: ui-serif, Georgia, "Times New Roman", serif;
|
|
32
|
+
--font-mono: ui-monospace, "SFMono-Regular", "SF Mono", "Menlo", "Monaco", "Consolas",
|
|
33
|
+
"Liberation Mono", "DejaVu Sans Mono", "Courier New", monospace;
|
|
34
|
+
|
|
35
|
+
/* Font weights */
|
|
36
|
+
--font-weight-normal: 400;
|
|
37
|
+
--font-weight-medium: 500;
|
|
38
|
+
--font-weight-semibold: 600;
|
|
39
|
+
--font-weight-bold: 700;
|
|
40
|
+
}
|
package/dist/es/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,8CAA8C","sourcesContent":["// Shared prop contract (sizes, colors, variants) for every Plex UI framework\n// package. Keep it free of framework imports.\n\nexport type Size =\n | \"5xs\"\n | \"4xs\"\n | \"3xs\"\n | \"2xs\"\n | \"xs\"\n | \"sm\"\n | \"md\"\n | \"lg\"\n | \"xl\"\n | \"2xl\"\n | \"3xl\"\n | \"4xl\"\n | \"5xl\"\n | \"6xl\"\nexport type Sizes<T extends Size = Size> = T\n\nexport type ControlSize = Sizes<\"3xs\" | \"2xs\" | \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\">\n\nexport type Radius = \"2xs\" | \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\" | \"full\"\nexport type Radii<T extends Radius = Radius> = T\n\nexport type TextColor = \"base\" | \"emphasis\" | \"secondary\" | \"tertiary\"\nexport type TextColors<T extends TextColor = TextColor> = T\n\nexport type SemanticColor =\n | \"primary\"\n | \"secondary\"\n | \"danger\"\n | \"success\"\n | \"warning\"\n | \"caution\"\n | \"discovery\"\n | \"info\"\nexport type SemanticColors<T extends SemanticColor = SemanticColor> = T\n\nexport type Variant = \"solid\" | \"soft\" | \"outline\" | \"ghost\"\nexport type Variants<T extends Variant = Variant> = T\n\nexport type Alignment = \"start\" | \"center\" | \"end\"\nexport type Alignments<T extends Alignment = Alignment> = T\n\nexport type FontWeight = \"inherit\" | \"normal\" | \"medium\" | \"semibold\" | \"bold\"\nexport type FontWeights<T extends FontWeight = FontWeight> = T\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugin for Plex UI.
|
|
3
|
+
*
|
|
4
|
+
* Importing `@plexui/styles/vite` (React apps can keep `@plexui/ui/vite`,
|
|
5
|
+
* which re-exports it) and adding `plexui()` to your `plugins` array
|
|
6
|
+
* eliminates every install-time gotcha documented in /docs/installation:
|
|
7
|
+
*
|
|
8
|
+
* 1. Pins `css.transformer = 'postcss'` so Lightning CSS doesn't
|
|
9
|
+
* reorder cascade layers strict-by-first-occurrence.
|
|
10
|
+
* 2. Pins `build.cssMinify = 'esbuild'` so Vite 8's Lightning CSS
|
|
11
|
+
* minifier doesn't strip the bare `@layer` declaration.
|
|
12
|
+
* 3. Injects `<style>@layer theme, base, components, utilities;</style>`
|
|
13
|
+
* into <head> as a runtime failsafe — the browser sees the layer
|
|
14
|
+
* order before any stylesheet rule arrives, regardless of how the
|
|
15
|
+
* bundler shuffles things.
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
*
|
|
19
|
+
* // vite.config.ts
|
|
20
|
+
* import { defineConfig } from 'vite';
|
|
21
|
+
* import plexui from '@plexui/styles/vite';
|
|
22
|
+
*
|
|
23
|
+
* export default defineConfig({
|
|
24
|
+
* plugins: [plexui()],
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* That replaces the manual `@layer …;` declaration in main.css and the
|
|
28
|
+
* manual `css.transformer` / `build.cssMinify` overrides. Your global
|
|
29
|
+
* stylesheet still needs `@import "@plexui/styles/css"` and `@source` —
|
|
30
|
+
* those wire the runtime tokens and Tailwind class-extraction, which a
|
|
31
|
+
* Vite plugin can't do for you.
|
|
32
|
+
*/
|
|
33
|
+
const LAYER_DECLARATION = "<style>@layer theme, base, components, utilities;</style>";
|
|
34
|
+
export default function plexui(options = {}) {
|
|
35
|
+
const { injectLayerOrder = true } = options;
|
|
36
|
+
return {
|
|
37
|
+
name: "@plexui/styles",
|
|
38
|
+
enforce: "pre",
|
|
39
|
+
config() {
|
|
40
|
+
return {
|
|
41
|
+
css: { transformer: "postcss" },
|
|
42
|
+
build: { cssMinify: "esbuild" },
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
transformIndexHtml(html) {
|
|
46
|
+
if (!injectLayerOrder)
|
|
47
|
+
return html;
|
|
48
|
+
// Place the layer declaration right after <head> so it lands
|
|
49
|
+
// before any bundler-injected <link rel="stylesheet">.
|
|
50
|
+
return html.replace(/(<head[^>]*>)/i, `$1\n ${LAYER_DECLARATION}`);
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/vite/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAkBH,MAAM,iBAAiB,GACrB,2DAA2D,CAAA;AAW7D,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,UAA+B,EAAE;IAC9D,MAAM,EAAE,gBAAgB,GAAG,IAAI,EAAE,GAAG,OAAO,CAAA;IAE3C,OAAO;QACL,IAAI,EAAE,gBAAgB;QACtB,OAAO,EAAE,KAAK;QACd,MAAM;YACJ,OAAO;gBACL,GAAG,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE;gBAC/B,KAAK,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE;aAChC,CAAA;QACH,CAAC;QACD,kBAAkB,CAAC,IAAI;YACrB,IAAI,CAAC,gBAAgB;gBAAE,OAAO,IAAI,CAAA;YAClC,6DAA6D;YAC7D,uDAAuD;YACvD,OAAO,IAAI,CAAC,OAAO,CACjB,gBAAgB,EAChB,WAAW,iBAAiB,EAAE,CAC/B,CAAA;QACH,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["/**\n * Vite plugin for Plex UI.\n *\n * Importing `@plexui/styles/vite` (React apps can keep `@plexui/ui/vite`,\n * which re-exports it) and adding `plexui()` to your `plugins` array\n * eliminates every install-time gotcha documented in /docs/installation:\n *\n * 1. Pins `css.transformer = 'postcss'` so Lightning CSS doesn't\n * reorder cascade layers strict-by-first-occurrence.\n * 2. Pins `build.cssMinify = 'esbuild'` so Vite 8's Lightning CSS\n * minifier doesn't strip the bare `@layer` declaration.\n * 3. Injects `<style>@layer theme, base, components, utilities;</style>`\n * into <head> as a runtime failsafe — the browser sees the layer\n * order before any stylesheet rule arrives, regardless of how the\n * bundler shuffles things.\n *\n * Usage:\n *\n * // vite.config.ts\n * import { defineConfig } from 'vite';\n * import plexui from '@plexui/styles/vite';\n *\n * export default defineConfig({\n * plugins: [plexui()],\n * });\n *\n * That replaces the manual `@layer …;` declaration in main.css and the\n * manual `css.transformer` / `build.cssMinify` overrides. Your global\n * stylesheet still needs `@import \"@plexui/styles/css\"` and `@source` —\n * those wire the runtime tokens and Tailwind class-extraction, which a\n * Vite plugin can't do for you.\n */\n\n// We deliberately do NOT import from \"vite\". Keeping vite out of the\n// build keeps it out of the runtime dependency graph entirely (the\n// plugin file is a few dozen lines and only describes the shape vite\n// expects). Consumers get full type-safety by declaring their config\n// `defineConfig({ plugins: [plexui()] })` — vite's own types cover the\n// `Plugin[]` slot, so our return value is just shaped to match it.\ntype ViteLikePlugin = {\n name: string\n enforce?: \"pre\" | \"post\"\n config?: () => {\n css?: { transformer?: \"postcss\" | \"lightningcss\" }\n build?: { cssMinify?: boolean | \"esbuild\" | \"lightningcss\" }\n }\n transformIndexHtml?: (html: string) => string\n}\n\nconst LAYER_DECLARATION =\n \"<style>@layer theme, base, components, utilities;</style>\"\n\nexport type PlexUIPluginOptions = {\n /**\n * Inject the cascade-layer `<style>` tag into <head> as a runtime\n * failsafe. Defaults to `true`. Disable only if you've already added\n * the equivalent `<style>` tag to your `index.html` by hand.\n */\n injectLayerOrder?: boolean\n}\n\nexport default function plexui(options: PlexUIPluginOptions = {}): ViteLikePlugin {\n const { injectLayerOrder = true } = options\n\n return {\n name: \"@plexui/styles\",\n enforce: \"pre\",\n config() {\n return {\n css: { transformer: \"postcss\" },\n build: { cssMinify: \"esbuild\" },\n }\n },\n transformIndexHtml(html) {\n if (!injectLayerOrder) return html\n // Place the layer declaration right after <head> so it lands\n // before any bundler-injected <link rel=\"stylesheet\">.\n return html.replace(\n /(<head[^>]*>)/i,\n `$1\\n ${LAYER_DECLARATION}`,\n )\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type Size = "5xs" | "4xs" | "3xs" | "2xs" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "5xl" | "6xl";
|
|
2
|
+
export type Sizes<T extends Size = Size> = T;
|
|
3
|
+
export type ControlSize = Sizes<"3xs" | "2xs" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl">;
|
|
4
|
+
export type Radius = "2xs" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "full";
|
|
5
|
+
export type Radii<T extends Radius = Radius> = T;
|
|
6
|
+
export type TextColor = "base" | "emphasis" | "secondary" | "tertiary";
|
|
7
|
+
export type TextColors<T extends TextColor = TextColor> = T;
|
|
8
|
+
export type SemanticColor = "primary" | "secondary" | "danger" | "success" | "warning" | "caution" | "discovery" | "info";
|
|
9
|
+
export type SemanticColors<T extends SemanticColor = SemanticColor> = T;
|
|
10
|
+
export type Variant = "solid" | "soft" | "outline" | "ghost";
|
|
11
|
+
export type Variants<T extends Variant = Variant> = T;
|
|
12
|
+
export type Alignment = "start" | "center" | "end";
|
|
13
|
+
export type Alignments<T extends Alignment = Alignment> = T;
|
|
14
|
+
export type FontWeight = "inherit" | "normal" | "medium" | "semibold" | "bold";
|
|
15
|
+
export type FontWeights<T extends FontWeight = FontWeight> = T;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugin for Plex UI.
|
|
3
|
+
*
|
|
4
|
+
* Importing `@plexui/styles/vite` (React apps can keep `@plexui/ui/vite`,
|
|
5
|
+
* which re-exports it) and adding `plexui()` to your `plugins` array
|
|
6
|
+
* eliminates every install-time gotcha documented in /docs/installation:
|
|
7
|
+
*
|
|
8
|
+
* 1. Pins `css.transformer = 'postcss'` so Lightning CSS doesn't
|
|
9
|
+
* reorder cascade layers strict-by-first-occurrence.
|
|
10
|
+
* 2. Pins `build.cssMinify = 'esbuild'` so Vite 8's Lightning CSS
|
|
11
|
+
* minifier doesn't strip the bare `@layer` declaration.
|
|
12
|
+
* 3. Injects `<style>@layer theme, base, components, utilities;</style>`
|
|
13
|
+
* into <head> as a runtime failsafe — the browser sees the layer
|
|
14
|
+
* order before any stylesheet rule arrives, regardless of how the
|
|
15
|
+
* bundler shuffles things.
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
*
|
|
19
|
+
* // vite.config.ts
|
|
20
|
+
* import { defineConfig } from 'vite';
|
|
21
|
+
* import plexui from '@plexui/styles/vite';
|
|
22
|
+
*
|
|
23
|
+
* export default defineConfig({
|
|
24
|
+
* plugins: [plexui()],
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* That replaces the manual `@layer …;` declaration in main.css and the
|
|
28
|
+
* manual `css.transformer` / `build.cssMinify` overrides. Your global
|
|
29
|
+
* stylesheet still needs `@import "@plexui/styles/css"` and `@source` —
|
|
30
|
+
* those wire the runtime tokens and Tailwind class-extraction, which a
|
|
31
|
+
* Vite plugin can't do for you.
|
|
32
|
+
*/
|
|
33
|
+
type ViteLikePlugin = {
|
|
34
|
+
name: string;
|
|
35
|
+
enforce?: "pre" | "post";
|
|
36
|
+
config?: () => {
|
|
37
|
+
css?: {
|
|
38
|
+
transformer?: "postcss" | "lightningcss";
|
|
39
|
+
};
|
|
40
|
+
build?: {
|
|
41
|
+
cssMinify?: boolean | "esbuild" | "lightningcss";
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
transformIndexHtml?: (html: string) => string;
|
|
45
|
+
};
|
|
46
|
+
export type PlexUIPluginOptions = {
|
|
47
|
+
/**
|
|
48
|
+
* Inject the cascade-layer `<style>` tag into <head> as a runtime
|
|
49
|
+
* failsafe. Defaults to `true`. Disable only if you've already added
|
|
50
|
+
* the equivalent `<style>` tag to your `index.html` by hand.
|
|
51
|
+
*/
|
|
52
|
+
injectLayerOrder?: boolean;
|
|
53
|
+
};
|
|
54
|
+
export default function plexui(options?: PlexUIPluginOptions): ViteLikePlugin;
|
|
55
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,111 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plexui/styles",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Framework-agnostic CSS for Plex UI: design tokens, globals and component styles",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"private": false,
|
|
8
|
+
"sideEffects": [
|
|
9
|
+
"*.css"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://plexui.com",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/plex-ui/ui.git",
|
|
15
|
+
"directory": "packages/styles"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"css",
|
|
19
|
+
"css-modules",
|
|
20
|
+
"design-system",
|
|
21
|
+
"design-tokens",
|
|
22
|
+
"tailwind",
|
|
23
|
+
"plexui",
|
|
24
|
+
"ui-kit"
|
|
25
|
+
],
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist/es/**",
|
|
31
|
+
"dist/types/**",
|
|
32
|
+
"postcss/*.mjs",
|
|
33
|
+
"!postcss/*.test.mjs",
|
|
34
|
+
"postcss.config.mjs"
|
|
35
|
+
],
|
|
36
|
+
"exports": {
|
|
37
|
+
"./css": "./dist/es/styles/index.css",
|
|
38
|
+
"./styles/*": "./dist/es/styles/*",
|
|
39
|
+
"./components/*": "./dist/es/components/*",
|
|
40
|
+
"./vite": {
|
|
41
|
+
"types": "./dist/types/vite/index.d.ts",
|
|
42
|
+
"default": "./dist/es/vite/index.js"
|
|
43
|
+
},
|
|
44
|
+
"./types": {
|
|
45
|
+
"types": "./dist/types/types.d.ts",
|
|
46
|
+
"default": "./dist/es/types.js"
|
|
47
|
+
},
|
|
48
|
+
"./postcss": "./postcss.config.mjs"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build:clean": "rm -rf dist",
|
|
52
|
+
"build:js": "tsc -p tsconfig.build.json",
|
|
53
|
+
"build:css": "node ./scripts/build-css.mjs",
|
|
54
|
+
"dev:js": "tsc -p tsconfig.build.json --watch --preserveWatchOutput",
|
|
55
|
+
"dev:css": "node ./scripts/build-css.mjs --watch",
|
|
56
|
+
"dev": "npm run build:clean && npm run build:js && npm run build:css && (npm run dev:js & npm run dev:css & wait)",
|
|
57
|
+
"build": "npm run build:clean && npm run build:js && npm run build:css",
|
|
58
|
+
"prepublishOnly": "npm run build"
|
|
59
|
+
},
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"@tailwindcss/postcss": "^4.0.10",
|
|
62
|
+
"autoprefixer": "^10.4.20",
|
|
63
|
+
"postcss": "^8",
|
|
64
|
+
"postcss-functions": "^4.0.2",
|
|
65
|
+
"postcss-mixins": "^11.0.3",
|
|
66
|
+
"postcss-nested": "^7.0.2",
|
|
67
|
+
"postcss-selector-parser": "^7.1.0",
|
|
68
|
+
"postcss-value-parser": "^4.2.0",
|
|
69
|
+
"tailwindcss": "^4.0.10"
|
|
70
|
+
},
|
|
71
|
+
"peerDependenciesMeta": {
|
|
72
|
+
"@tailwindcss/postcss": {
|
|
73
|
+
"optional": true
|
|
74
|
+
},
|
|
75
|
+
"autoprefixer": {
|
|
76
|
+
"optional": true
|
|
77
|
+
},
|
|
78
|
+
"postcss": {
|
|
79
|
+
"optional": true
|
|
80
|
+
},
|
|
81
|
+
"postcss-functions": {
|
|
82
|
+
"optional": true
|
|
83
|
+
},
|
|
84
|
+
"postcss-mixins": {
|
|
85
|
+
"optional": true
|
|
86
|
+
},
|
|
87
|
+
"postcss-nested": {
|
|
88
|
+
"optional": true
|
|
89
|
+
},
|
|
90
|
+
"postcss-selector-parser": {
|
|
91
|
+
"optional": true
|
|
92
|
+
},
|
|
93
|
+
"postcss-value-parser": {
|
|
94
|
+
"optional": true
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"devDependencies": {
|
|
98
|
+
"@tailwindcss/postcss": "4.0.17",
|
|
99
|
+
"@types/postcss-functions": "4.0.4",
|
|
100
|
+
"@types/postcss-mixins": "9.0.6",
|
|
101
|
+
"autoprefixer": "10.4.20",
|
|
102
|
+
"postcss": "^8",
|
|
103
|
+
"postcss-functions": "^4.0.2",
|
|
104
|
+
"postcss-mixins": "^11.0.3",
|
|
105
|
+
"postcss-nested": "^7.0.2",
|
|
106
|
+
"postcss-selector-parser": "7.1.0",
|
|
107
|
+
"postcss-value-parser": "4.2.0",
|
|
108
|
+
"tailwindcss": "4.0.10",
|
|
109
|
+
"typescript": "5.7.3"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import postcssFunctions from "postcss-functions"
|
|
2
|
+
|
|
3
|
+
/** @type {import('postcss').PluginCreator} */
|
|
4
|
+
export default function platformUIFunctions() {
|
|
5
|
+
return {
|
|
6
|
+
...postcssFunctions({
|
|
7
|
+
functions: {
|
|
8
|
+
alpha: (value, amount) => {
|
|
9
|
+
const formattedAmount = amount.includes("%")
|
|
10
|
+
? amount
|
|
11
|
+
: amount.startsWith(".") || amount.startsWith("0.")
|
|
12
|
+
? `${parseFloat(amount) * 100}%`
|
|
13
|
+
: `${amount}%`
|
|
14
|
+
return `color-mix(in oklab, ${value} ${formattedAmount}, transparent)`
|
|
15
|
+
},
|
|
16
|
+
spacing: (value) => `calc(var(--spacing) * ${value})`,
|
|
17
|
+
},
|
|
18
|
+
}),
|
|
19
|
+
postcssPlugin: "plexui/components/postcss-functions",
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
platformUIFunctions.postcss = true
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import postcss from "postcss"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import('postcss').PluginCreator<{ breakpoints: Record<string, number> }>} PostcssPluginCreator
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Injects the breakpoint variables into the CSS. Must run before tailwindcss.
|
|
10
|
+
*
|
|
11
|
+
* @type {PostcssPluginCreator}
|
|
12
|
+
*/
|
|
13
|
+
export default function injectBreakpoints(
|
|
14
|
+
/** @type {{ breakpoints: Record<string, number> }} */
|
|
15
|
+
{ breakpoints },
|
|
16
|
+
) {
|
|
17
|
+
return {
|
|
18
|
+
postcssPlugin: "plexui/components/inject-breakpoints",
|
|
19
|
+
Once(root) {
|
|
20
|
+
root.walkComments((comment) => {
|
|
21
|
+
// Look for comment containing "@inject_breakpoints"
|
|
22
|
+
if (comment.text.trim() === "@inject_breakpoints") {
|
|
23
|
+
// Generate breakpoint declarations
|
|
24
|
+
const declarations = []
|
|
25
|
+
for (const [key, value] of Object.entries(breakpoints)) {
|
|
26
|
+
declarations.push(
|
|
27
|
+
postcss.decl({
|
|
28
|
+
prop: `--breakpoint-${key}`,
|
|
29
|
+
value: `${value}px`,
|
|
30
|
+
}),
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Insert all declarations after the comment
|
|
35
|
+
declarations.reverse().forEach((decl) => {
|
|
36
|
+
comment.parent.insertAfter(comment, decl)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// Remove the comment
|
|
40
|
+
comment.remove()
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
injectBreakpoints.postcss = true
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import postcssMixins from "postcss-mixins"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import('postcss').PluginCreator<{ breakpoints: Record<string, number> }>} PostcssPluginCreator
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** @type {PostcssPluginCreator} */
|
|
9
|
+
export default function platformUIMixins(
|
|
10
|
+
/** @type {{ breakpoints: Record<string, number> }} */
|
|
11
|
+
{ breakpoints },
|
|
12
|
+
) {
|
|
13
|
+
/** @type {Record<string, string | undefined>} */
|
|
14
|
+
const mediaQueries = Object.entries(breakpoints).reduce((acc, [key, value]) => {
|
|
15
|
+
acc[key] = `(min-width: ${value}px)`
|
|
16
|
+
return acc
|
|
17
|
+
}, {})
|
|
18
|
+
|
|
19
|
+
const supportedBreakpoints = Object.keys(mediaQueries)
|
|
20
|
+
.join(", ")
|
|
21
|
+
.replace(/, ([^,]*)$/, " and $1")
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
...postcssMixins({
|
|
25
|
+
mixins: {
|
|
26
|
+
// @mixin breakpoint md { ... } // scoped to current selector (default)
|
|
27
|
+
// @mixin breakpoint md global { ... } // global (no selector wrapper)
|
|
28
|
+
breakpoint(_, targetBreakpointAndScope) {
|
|
29
|
+
const [breakpointName, scope = "scoped"] = targetBreakpointAndScope.trim().split(" ")
|
|
30
|
+
|
|
31
|
+
const mediaQuery = mediaQueries[breakpointName]
|
|
32
|
+
|
|
33
|
+
if (!mediaQuery) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
`"${breakpointName}" is not a valid breakpoint. Supported breakpoints are ${supportedBreakpoints}.`,
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const isGlobal = typeof scope === "string" && scope.toLowerCase() === "global"
|
|
40
|
+
|
|
41
|
+
return isGlobal
|
|
42
|
+
? {
|
|
43
|
+
[`@media ${mediaQuery}`]: {
|
|
44
|
+
"@mixin-content": {},
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
: {
|
|
48
|
+
[`@media ${mediaQuery}`]: {
|
|
49
|
+
"&": {
|
|
50
|
+
"@mixin-content": {},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
light() {
|
|
57
|
+
return {
|
|
58
|
+
':where([data-theme="light"]) &': {
|
|
59
|
+
"@mixin-content": {},
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
dark() {
|
|
65
|
+
return {
|
|
66
|
+
':where([data-theme="dark"]) &': {
|
|
67
|
+
"@mixin-content": {},
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
hover() {
|
|
73
|
+
return {
|
|
74
|
+
"@media (hover: hover) and (pointer: fine)": {
|
|
75
|
+
"&:hover": {
|
|
76
|
+
"@mixin-content": {},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
autofill() {
|
|
83
|
+
return {
|
|
84
|
+
"&:-webkit-autofill, &:-webkit-autofill:focus, &:-webkit-autofill:hover": {
|
|
85
|
+
"@mixin-content": {},
|
|
86
|
+
},
|
|
87
|
+
// Identical to block above, but must keep separate for browsers to recognize their own selector
|
|
88
|
+
"&:-webkit-autofill-and-obscured, &:-webkit-autofill-strong-password, &:-webkit-autofill-strong-password-viewable":
|
|
89
|
+
{
|
|
90
|
+
"@mixin-content": {},
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
}),
|
|
96
|
+
postcssPlugin: "plexui/components/postcss-mixins",
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
platformUIMixins.postcss = true
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import postcss from "postcss"
|
|
2
|
+
import selectorParser from "postcss-selector-parser"
|
|
3
|
+
import valueParser from "postcss-value-parser"
|
|
4
|
+
|
|
5
|
+
/** @type {import('postcss').PluginCreator} */
|
|
6
|
+
export default function platformUILightDark() {
|
|
7
|
+
return {
|
|
8
|
+
postcssPlugin: "plexui/components/parse-light-dark",
|
|
9
|
+
OnceExit(root) {
|
|
10
|
+
const stringifyThemeValue = (tokens, which) =>
|
|
11
|
+
tokens.map((tok) => (tok.isLD ? tok[which] : tok.raw)).join("")
|
|
12
|
+
// Split selector list into individual strings using selector parser
|
|
13
|
+
const splitSelectorList = (selector) => {
|
|
14
|
+
const list = []
|
|
15
|
+
selectorParser((rootSel) => {
|
|
16
|
+
rootSel.each((sel) => list.push(sel.toString().trim()))
|
|
17
|
+
}).processSync(selector)
|
|
18
|
+
return list
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Determine if selector is exactly :root or [data-theme], optionally wrapped in :where()
|
|
22
|
+
const isSimpleThemeScopeUsingParser = (selectorStr) => {
|
|
23
|
+
let result = false
|
|
24
|
+
selectorParser((rootSel) => {
|
|
25
|
+
if (rootSel.nodes.length !== 1) return
|
|
26
|
+
const sel = rootSel.nodes[0]
|
|
27
|
+
const onlyNode = () => (sel.nodes.length === 1 ? sel.nodes[0] : null)
|
|
28
|
+
const isSimpleNode = (node) =>
|
|
29
|
+
!!node &&
|
|
30
|
+
((node.type === "pseudo" && node.value === ":root") ||
|
|
31
|
+
(node.type === "attribute" && node.attribute === "data-theme" && !node.operator))
|
|
32
|
+
// Direct simple
|
|
33
|
+
const n = onlyNode()
|
|
34
|
+
if (isSimpleNode(n)) {
|
|
35
|
+
result = true
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
// :where(simple)
|
|
39
|
+
if (n && n.type === "pseudo" && n.value === ":where" && n.nodes && n.nodes.length === 1) {
|
|
40
|
+
const innerSel = n.nodes[0]
|
|
41
|
+
if (innerSel.nodes.length === 1 && isSimpleNode(innerSel.nodes[0])) {
|
|
42
|
+
result = true
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}).processSync(selectorStr)
|
|
46
|
+
return result
|
|
47
|
+
}
|
|
48
|
+
/** @type {Map<import('postcss').Rule, Array<{ prop: string; full: Array<any>; important: boolean }>>} */
|
|
49
|
+
const themeMap = new Map()
|
|
50
|
+
|
|
51
|
+
// 1) Collect & remove light-dark() decls, capturing !important
|
|
52
|
+
root.walkDecls((decl) => {
|
|
53
|
+
if (!decl.value.includes("light-dark(")) return
|
|
54
|
+
|
|
55
|
+
const parsed = valueParser(decl.value)
|
|
56
|
+
let sawLD = false
|
|
57
|
+
const tokens = []
|
|
58
|
+
|
|
59
|
+
for (const node of parsed.nodes) {
|
|
60
|
+
if (node.type === "function" && node.value === "light-dark") {
|
|
61
|
+
// inside node.nodes we expect [lightArg, comma, darkArg]
|
|
62
|
+
const [lightNode, , darkNode] = node.nodes
|
|
63
|
+
const light = valueParser.stringify(lightNode).trim()
|
|
64
|
+
const dark = valueParser.stringify(darkNode).trim()
|
|
65
|
+
tokens.push({ isLD: true, light, dark })
|
|
66
|
+
sawLD = true
|
|
67
|
+
} else {
|
|
68
|
+
tokens.push({ isLD: false, raw: valueParser.stringify(node) })
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (!sawLD) return
|
|
72
|
+
|
|
73
|
+
// store entries
|
|
74
|
+
const arr = themeMap.get(decl.parent) || []
|
|
75
|
+
arr.push({ prop: decl.prop, full: tokens, important: decl.important })
|
|
76
|
+
themeMap.set(decl.parent, arr)
|
|
77
|
+
decl.remove()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
// 2) Emit transformed rules
|
|
81
|
+
const isSimpleThemeScope = (s) => isSimpleThemeScopeUsingParser(s)
|
|
82
|
+
const appendThemeRules = (parent, afterNode, entries, lightSelector, darkSelector) => {
|
|
83
|
+
const lightRule = postcss.rule({ selector: lightSelector })
|
|
84
|
+
const darkRule = postcss.rule({ selector: darkSelector })
|
|
85
|
+
for (const { prop, full, important } of entries) {
|
|
86
|
+
lightRule.append(
|
|
87
|
+
postcss.decl({ prop, value: stringifyThemeValue(full, "light"), important }),
|
|
88
|
+
)
|
|
89
|
+
darkRule.append(
|
|
90
|
+
postcss.decl({ prop, value: stringifyThemeValue(full, "dark"), important }),
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
parent.insertAfter(afterNode, lightRule)
|
|
94
|
+
parent.insertAfter(lightRule, darkRule)
|
|
95
|
+
}
|
|
96
|
+
for (const [rule, entries] of themeMap) {
|
|
97
|
+
const parent = rule.parent
|
|
98
|
+
if (!parent || !rule.selector) continue
|
|
99
|
+
|
|
100
|
+
// root selector
|
|
101
|
+
if (rule.selector === ":root") {
|
|
102
|
+
appendThemeRules(
|
|
103
|
+
parent,
|
|
104
|
+
rule,
|
|
105
|
+
entries,
|
|
106
|
+
':where(:root), :where([data-theme="light"])',
|
|
107
|
+
':where([data-theme="dark"])',
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
if (rule.nodes.length === 0) {
|
|
111
|
+
rule.remove()
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
// non-root selector: flatten to top-level grouped theme rules
|
|
115
|
+
const selList = splitSelectorList(rule.selector)
|
|
116
|
+
|
|
117
|
+
// Special-case: when selectors are only top-level theme scopes
|
|
118
|
+
// (:root and/or [data-theme]), treat them like :root by replacing
|
|
119
|
+
// with :where(:root) and :where([data-theme="light"]) instead of nesting.
|
|
120
|
+
const simpleScopes = selList.filter((s) => isSimpleThemeScope(s))
|
|
121
|
+
const otherSelectors = selList.filter((s) => !isSimpleThemeScope(s))
|
|
122
|
+
if (simpleScopes.length > 0 && otherSelectors.length === 0) {
|
|
123
|
+
appendThemeRules(
|
|
124
|
+
parent,
|
|
125
|
+
rule,
|
|
126
|
+
entries,
|
|
127
|
+
':where(:root), :where([data-theme="light"])',
|
|
128
|
+
':where([data-theme="dark"])',
|
|
129
|
+
)
|
|
130
|
+
if (rule.nodes.length === 0) {
|
|
131
|
+
rule.remove()
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
// Mixed or no simple scopes.
|
|
135
|
+
// - Include base selectors (light defaults) ONLY when there are no simple scopes.
|
|
136
|
+
// - Never nest simple scopes; instead add bare replacements.
|
|
137
|
+
const lightParts = []
|
|
138
|
+
const darkParts = []
|
|
139
|
+
|
|
140
|
+
// Base selectors (light defaults) only if no simple scopes are present
|
|
141
|
+
if (simpleScopes.length === 0) {
|
|
142
|
+
lightParts.push(...selList)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Bare replacements for simple scopes
|
|
146
|
+
if (simpleScopes.includes(":root")) {
|
|
147
|
+
lightParts.push(":where(:root)")
|
|
148
|
+
}
|
|
149
|
+
if (simpleScopes.length > 0) {
|
|
150
|
+
lightParts.push(':where([data-theme="light"])')
|
|
151
|
+
darkParts.push(':where([data-theme="dark"])')
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Themed nested only for non-simple selectors
|
|
155
|
+
lightParts.push(...otherSelectors.map((s) => `:where([data-theme="light"]) ${s}`))
|
|
156
|
+
darkParts.push(...otherSelectors.map((s) => `:where([data-theme="dark"]) ${s}`))
|
|
157
|
+
|
|
158
|
+
// Dedupe while preserving order
|
|
159
|
+
const uniq = (arr) => Array.from(new Set(arr))
|
|
160
|
+
const lightSelector = uniq(lightParts).join(", ")
|
|
161
|
+
const darkSelector = uniq(darkParts).join(", ")
|
|
162
|
+
|
|
163
|
+
appendThemeRules(parent, rule, entries, lightSelector, darkSelector)
|
|
164
|
+
if (rule.nodes.length === 0) {
|
|
165
|
+
rule.remove()
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
platformUILightDark.postcss = true
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import postcss from "postcss"
|
|
3
|
+
|
|
4
|
+
/** @type {import('postcss').PluginCreator<{ layer?: string }>} */
|
|
5
|
+
export default function wrapModulesInLayer({ layer = "components" } = {}) {
|
|
6
|
+
return {
|
|
7
|
+
postcssPlugin: "plexui/components/postcss-wrap-modules-in-layer",
|
|
8
|
+
Once(root, { result }) {
|
|
9
|
+
const file = result.opts.from
|
|
10
|
+
if (file && /\.module\.css$/.test(file)) {
|
|
11
|
+
const layerAtRule = postcss.atRule({ name: "layer", params: layer })
|
|
12
|
+
while (root.first) {
|
|
13
|
+
layerAtRule.append(root.first)
|
|
14
|
+
}
|
|
15
|
+
root.append(layerAtRule)
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
wrapModulesInLayer.postcss = true
|