refract-ui 0.0.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Param Mehta
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # refract-ui
2
+
3
+ Themeable React component library — extracted from [parammehta.com](https://parammehta.com).
4
+ Primitives, form controls, and a couple of Three.js pieces (device models, a
5
+ displacement carousel), with no Next.js or portfolio-specific coupling.
6
+
7
+ Storybook: https://storybook.parammehta.com
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm i refract-ui
13
+ ```
14
+
15
+ `react`, `react-dom`, and `framer-motion` are peer dependencies. `three` and
16
+ `three-stdlib` are optional peers, only required if you use `refract-ui/model`
17
+ or `refract-ui/carousel`.
18
+
19
+ ## Usage
20
+
21
+ ```tsx
22
+ import { Text, ThemeProvider, tokenStyles } from 'refract-ui';
23
+ import 'refract-ui/styles.css';
24
+
25
+ function App() {
26
+ return (
27
+ <ThemeProvider>
28
+ <Text size="l">Hello</Text>
29
+ </ThemeProvider>
30
+ );
31
+ }
32
+ ```
33
+
34
+ Required stylesheet: `refract-ui/styles.css` (component styles + a couple of
35
+ shared keyframes). Optional: `refract-ui/reset.css` (a minimal CSS reset) and
36
+ `refract-ui/media.css` (the breakpoint contract as `@custom-media`, for
37
+ consumers running their own `postcss-preset-env`).
38
+
39
+ ### Routing
40
+
41
+ Components that render links (`Link`, `Button`, `Breadcrumbs`) default to a
42
+ plain `<a>`. To route through your framework's link component, wrap your app
43
+ in `LinkProvider`:
44
+
45
+ ```tsx
46
+ import { LinkProvider } from 'refract-ui';
47
+ import NextLink from 'next/link';
48
+ import { forwardRef } from 'react';
49
+
50
+ const NextLinkAdapter = forwardRef((props, ref) => <NextLink {...props} ref={ref} />);
51
+
52
+ <LinkProvider component={NextLinkAdapter}>{/* ... */}</LinkProvider>;
53
+ ```
54
+
55
+ ### Fonts
56
+
57
+ No fonts are bundled. `--fontStack` falls back to `--brandFontStack` (unset by
58
+ default) then the system font stack — set `--brandFontStack` to use your own
59
+ brand typeface.
60
+
61
+ ### 3D components (`Model`, `Carousel`)
62
+
63
+ These use `three`/`three-stdlib` and load `.glb` device models via a DRACO
64
+ decoder. Both are optional peer dependencies — install them yourself, then:
65
+
66
+ ```bash
67
+ cp -R node_modules/three/examples/jsm/libs/draco/gltf/ public/draco/
68
+ cp node_modules/refract-ui/dist/assets/*.glb public/models/
69
+ ```
70
+
71
+ Both paths are configurable via `RefractProvider`'s `dracoDecoderPath` and
72
+ `modelBasePath` props if you serve them elsewhere.
73
+
74
+ ## Development
75
+
76
+ ```bash
77
+ npm install
78
+ npm run storybook # dev, localhost:9009
79
+ npm run build # vite build -> dist/
80
+ npm test # vitest
81
+ npm run lint # eslint
82
+ npm run stylelint
83
+ npm run typecheck
84
+ ```
85
+
86
+ ## Publishing
87
+
88
+ Versioning and npm publishing are automated with release-please + npm trusted
89
+ publishing (GitHub Actions OIDC) — see `.github/workflows/`. Pushing a
90
+ Conventional Commit to `main` is enough; there is no manual publish step.
package/dist/.gitkeep ADDED
File without changes
@@ -0,0 +1,2 @@
1
+ "use client";"use strict";
2
+ //# sourceMappingURL=carousel.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"carousel.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ //# sourceMappingURL=carousel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"carousel.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';
2
+ export interface TextProps extends ComponentPropsWithoutRef<'span'> {
3
+ children?: ReactNode;
4
+ size?: 's' | 'm' | 'l' | 'xl';
5
+ as?: ElementType;
6
+ align?: string;
7
+ weight?: string;
8
+ secondary?: boolean;
9
+ }
10
+ export declare const Text: ({ children, size, as: Component, align, weight, secondary, className, ...rest }: TextProps) => import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ export * from './Text';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ "use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("react/jsx-runtime"),p={desktop:2080,laptop:1680,tablet:1040,mobile:696,mobileS:400},T=e=>Number(e.replace("px","")),c=e=>`${e}px`,x=e=>`${e/16}rem`,d=e=>Number(e.replace("ms","")),m=e=>`${e}ms`,y=e=>(e==null?void 0:e.split(" ").map(s=>Number(s)/255))||[];function f(e,s={}){const n={},a=Object.keys(e);for(const o of a){let t=e[o];typeof t=="number"&&o==="delay"&&(t=m(t)),typeof t=="number"&&o!=="opacity"&&(t=c(t)),n[`--${o}`]=t}return{...n,...s}}function u(...e){return e.filter(Boolean).join(" ")}const b="_text_18l38_1",N={text:b},j=({children:e,size:s="m",as:n="span",align:a="auto",weight:o="auto",secondary:t,className:r,...l})=>i.jsx(n,{className:u(N.text,r),"data-align":a,"data-size":s,"data-weight":o,"data-secondary":t,...l,children:e});exports.Text=j;exports.classes=u;exports.cssProps=f;exports.media=p;exports.msToNum=d;exports.numToMs=m;exports.numToPx=c;exports.pxToNum=T;exports.pxToRem=x;exports.rgbToThreeColor=y;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/utils/style.ts","../src/components/Text/Text.tsx"],"sourcesContent":["import { CSSProperties } from 'react';\n\n/**\n * Media query breakpoints\n */\nexport const media = {\n desktop: 2080,\n laptop: 1680,\n tablet: 1040,\n mobile: 696,\n mobileS: 400,\n} as const;\n\n/**\n * Convert a px string to a number\n */\nexport const pxToNum = (px: string): number => Number(px.replace('px', ''));\n\n/**\n * Convert a number to a px string\n */\nexport const numToPx = (num: number): string => `${num}px`;\n\n/**\n * Convert pixel values to rem for a11y\n */\nexport const pxToRem = (px: number): string => `${px / 16}rem`;\n\n/**\n * Convert ms token values to a raw numbers for ReactTransitionGroup\n * Transition delay props\n */\nexport const msToNum = (msString: string): number => Number(msString.replace('ms', ''));\n\n/**\n * Convert a number to an ms string\n */\nexport const numToMs = (num: number): string => `${num}ms`;\n\n/**\n * Convert an rgb theme property (e.g. rgbBlack: '0 0 0')\n * to values that can be spread into a ThreeJS Color class\n */\nexport const rgbToThreeColor = (rgb: string | undefined): number[] =>\n rgb?.split(' ').map(value => Number(value) / 255) || [];\n\n/**\n * Convert a JS object into `--` prefixed css custom properties.\n * Optionally pass a second param for normal styles\n */\nexport function cssProps(\n props: Record<string, string | number>,\n style: CSSProperties = {}\n): Record<string, string | number> & CSSProperties {\n const result: Record<string, string | number> = {};\n\n const keys = Object.keys(props);\n\n for (const key of keys) {\n let value: string | number = props[key];\n\n if (typeof value === 'number' && key === 'delay') {\n value = numToMs(value);\n }\n\n if (typeof value === 'number' && key !== 'opacity') {\n value = numToPx(value);\n }\n\n result[`--${key}`] = value;\n }\n\n return { ...result, ...style };\n}\n\n/**\n * Concatenate classNames together\n */\nexport function classes(...args: (string | false | undefined | null)[]): string {\n return args.filter(Boolean).join(' ');\n}\n","import { type ComponentPropsWithoutRef, type ElementType, type ReactNode } from 'react';\nimport { classes } from '../../utils/style';\nimport styles from './Text.module.css';\n\nexport interface TextProps extends ComponentPropsWithoutRef<'span'> {\n children?: ReactNode;\n size?: 's' | 'm' | 'l' | 'xl';\n as?: ElementType;\n align?: string;\n weight?: string;\n secondary?: boolean;\n}\n\nexport const Text = ({\n children,\n size = 'm',\n as: Component = 'span',\n align = 'auto',\n weight = 'auto',\n secondary,\n className,\n ...rest\n}: TextProps) => {\n return (\n <Component\n className={classes(styles.text, className)}\n data-align={align}\n data-size={size}\n data-weight={weight}\n data-secondary={secondary}\n {...rest}\n >\n {children}\n </Component>\n );\n};\n"],"names":["media","pxToNum","px","numToPx","num","pxToRem","msToNum","msString","numToMs","rgbToThreeColor","rgb","value","cssProps","props","style","result","keys","key","classes","args","Text","children","size","Component","align","weight","secondary","className","rest","jsx","styles"],"mappings":"kIAKaA,EAAQ,CACnB,QAAS,KACT,OAAQ,KACR,OAAQ,KACR,OAAQ,IACR,QAAS,GACX,EAKaC,EAAWC,GAAuB,OAAOA,EAAG,QAAQ,KAAM,EAAE,CAAC,EAK7DC,EAAWC,GAAwB,GAAGA,CAAG,KAKzCC,EAAWH,GAAuB,GAAGA,EAAK,EAAE,MAM5CI,EAAWC,GAA6B,OAAOA,EAAS,QAAQ,KAAM,EAAE,CAAC,EAKzEC,EAAWJ,GAAwB,GAAGA,CAAG,KAMzCK,EAAmBC,IAC9BA,GAAA,YAAAA,EAAK,MAAM,KAAK,IAAIC,GAAS,OAAOA,CAAK,EAAI,OAAQ,CAAA,EAMhD,SAASC,EACdC,EACAC,EAAuB,GAC0B,CACjD,MAAMC,EAA0C,CAAA,EAE1CC,EAAO,OAAO,KAAKH,CAAK,EAE9B,UAAWI,KAAOD,EAAM,CACtB,IAAIL,EAAyBE,EAAMI,CAAG,EAElC,OAAON,GAAU,UAAYM,IAAQ,UACvCN,EAAQH,EAAQG,CAAK,GAGnB,OAAOA,GAAU,UAAYM,IAAQ,YACvCN,EAAQR,EAAQQ,CAAK,GAGvBI,EAAO,KAAKE,CAAG,EAAE,EAAIN,CACvB,CAEA,MAAO,CAAE,GAAGI,EAAQ,GAAGD,CAAA,CACzB,CAKO,SAASI,KAAWC,EAAqD,CAC9E,OAAOA,EAAK,OAAO,OAAO,EAAE,KAAK,GAAG,CACtC,oCCnEaC,EAAO,CAAC,CACnB,SAAAC,EACA,KAAAC,EAAO,IACP,GAAIC,EAAY,OAChB,MAAAC,EAAQ,OACR,OAAAC,EAAS,OACT,UAAAC,EACA,UAAAC,EACA,GAAGC,CACL,IAEIC,EAAAA,IAACN,EAAA,CACC,UAAWL,EAAQY,EAAO,KAAMH,CAAS,EACzC,aAAYH,EACZ,YAAWF,EACX,cAAaG,EACb,iBAAgBC,EACf,GAAGE,EAEH,SAAAP,CAAA,CAAA"}
@@ -0,0 +1,2 @@
1
+ export * from './components/Text';
2
+ export * from './utils/style';
package/dist/index.js ADDED
@@ -0,0 +1,56 @@
1
+ "use client";
2
+ import { jsx as l } from "react/jsx-runtime";
3
+ const y = {
4
+ desktop: 2080,
5
+ laptop: 1680,
6
+ tablet: 1040,
7
+ mobile: 696,
8
+ mobileS: 400
9
+ }, d = (t) => Number(t.replace("px", "")), r = (t) => `${t}px`, T = (t) => `${t / 16}rem`, b = (t) => Number(t.replace("ms", "")), u = (t) => `${t}ms`, N = (t) => (t == null ? void 0 : t.split(" ").map((s) => Number(s) / 255)) || [];
10
+ function k(t, s = {}) {
11
+ const n = {}, a = Object.keys(t);
12
+ for (const o of a) {
13
+ let e = t[o];
14
+ typeof e == "number" && o === "delay" && (e = u(e)), typeof e == "number" && o !== "opacity" && (e = r(e)), n[`--${o}`] = e;
15
+ }
16
+ return { ...n, ...s };
17
+ }
18
+ function i(...t) {
19
+ return t.filter(Boolean).join(" ");
20
+ }
21
+ const p = "_text_18l38_1", x = {
22
+ text: p
23
+ }, $ = ({
24
+ children: t,
25
+ size: s = "m",
26
+ as: n = "span",
27
+ align: a = "auto",
28
+ weight: o = "auto",
29
+ secondary: e,
30
+ className: c,
31
+ ...m
32
+ }) => /* @__PURE__ */ l(
33
+ n,
34
+ {
35
+ className: i(x.text, c),
36
+ "data-align": a,
37
+ "data-size": s,
38
+ "data-weight": o,
39
+ "data-secondary": e,
40
+ ...m,
41
+ children: t
42
+ }
43
+ );
44
+ export {
45
+ $ as Text,
46
+ i as classes,
47
+ k as cssProps,
48
+ y as media,
49
+ b as msToNum,
50
+ u as numToMs,
51
+ r as numToPx,
52
+ d as pxToNum,
53
+ T as pxToRem,
54
+ N as rgbToThreeColor
55
+ };
56
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/utils/style.ts","../src/components/Text/Text.tsx"],"sourcesContent":["import { CSSProperties } from 'react';\n\n/**\n * Media query breakpoints\n */\nexport const media = {\n desktop: 2080,\n laptop: 1680,\n tablet: 1040,\n mobile: 696,\n mobileS: 400,\n} as const;\n\n/**\n * Convert a px string to a number\n */\nexport const pxToNum = (px: string): number => Number(px.replace('px', ''));\n\n/**\n * Convert a number to a px string\n */\nexport const numToPx = (num: number): string => `${num}px`;\n\n/**\n * Convert pixel values to rem for a11y\n */\nexport const pxToRem = (px: number): string => `${px / 16}rem`;\n\n/**\n * Convert ms token values to a raw numbers for ReactTransitionGroup\n * Transition delay props\n */\nexport const msToNum = (msString: string): number => Number(msString.replace('ms', ''));\n\n/**\n * Convert a number to an ms string\n */\nexport const numToMs = (num: number): string => `${num}ms`;\n\n/**\n * Convert an rgb theme property (e.g. rgbBlack: '0 0 0')\n * to values that can be spread into a ThreeJS Color class\n */\nexport const rgbToThreeColor = (rgb: string | undefined): number[] =>\n rgb?.split(' ').map(value => Number(value) / 255) || [];\n\n/**\n * Convert a JS object into `--` prefixed css custom properties.\n * Optionally pass a second param for normal styles\n */\nexport function cssProps(\n props: Record<string, string | number>,\n style: CSSProperties = {}\n): Record<string, string | number> & CSSProperties {\n const result: Record<string, string | number> = {};\n\n const keys = Object.keys(props);\n\n for (const key of keys) {\n let value: string | number = props[key];\n\n if (typeof value === 'number' && key === 'delay') {\n value = numToMs(value);\n }\n\n if (typeof value === 'number' && key !== 'opacity') {\n value = numToPx(value);\n }\n\n result[`--${key}`] = value;\n }\n\n return { ...result, ...style };\n}\n\n/**\n * Concatenate classNames together\n */\nexport function classes(...args: (string | false | undefined | null)[]): string {\n return args.filter(Boolean).join(' ');\n}\n","import { type ComponentPropsWithoutRef, type ElementType, type ReactNode } from 'react';\nimport { classes } from '../../utils/style';\nimport styles from './Text.module.css';\n\nexport interface TextProps extends ComponentPropsWithoutRef<'span'> {\n children?: ReactNode;\n size?: 's' | 'm' | 'l' | 'xl';\n as?: ElementType;\n align?: string;\n weight?: string;\n secondary?: boolean;\n}\n\nexport const Text = ({\n children,\n size = 'm',\n as: Component = 'span',\n align = 'auto',\n weight = 'auto',\n secondary,\n className,\n ...rest\n}: TextProps) => {\n return (\n <Component\n className={classes(styles.text, className)}\n data-align={align}\n data-size={size}\n data-weight={weight}\n data-secondary={secondary}\n {...rest}\n >\n {children}\n </Component>\n );\n};\n"],"names":["media","pxToNum","px","numToPx","num","pxToRem","msToNum","msString","numToMs","rgbToThreeColor","rgb","value","cssProps","props","style","result","keys","key","classes","args","Text","children","size","Component","align","weight","secondary","className","rest","jsx","styles"],"mappings":";;AAKO,MAAMA,IAAQ;AAAA,EACnB,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AACX,GAKaC,IAAU,CAACC,MAAuB,OAAOA,EAAG,QAAQ,MAAM,EAAE,CAAC,GAK7DC,IAAU,CAACC,MAAwB,GAAGA,CAAG,MAKzCC,IAAU,CAACH,MAAuB,GAAGA,IAAK,EAAE,OAM5CI,IAAU,CAACC,MAA6B,OAAOA,EAAS,QAAQ,MAAM,EAAE,CAAC,GAKzEC,IAAU,CAACJ,MAAwB,GAAGA,CAAG,MAMzCK,IAAkB,CAACC,OAC9BA,KAAA,gBAAAA,EAAK,MAAM,KAAK,IAAI,CAAAC,MAAS,OAAOA,CAAK,IAAI,SAAQ,CAAA;AAMhD,SAASC,EACdC,GACAC,IAAuB,IAC0B;AACjD,QAAMC,IAA0C,CAAA,GAE1CC,IAAO,OAAO,KAAKH,CAAK;AAE9B,aAAWI,KAAOD,GAAM;AACtB,QAAIL,IAAyBE,EAAMI,CAAG;AAEtC,IAAI,OAAON,KAAU,YAAYM,MAAQ,YACvCN,IAAQH,EAAQG,CAAK,IAGnB,OAAOA,KAAU,YAAYM,MAAQ,cACvCN,IAAQR,EAAQQ,CAAK,IAGvBI,EAAO,KAAKE,CAAG,EAAE,IAAIN;AAAA,EACvB;AAEA,SAAO,EAAE,GAAGI,GAAQ,GAAGD,EAAA;AACzB;AAKO,SAASI,KAAWC,GAAqD;AAC9E,SAAOA,EAAK,OAAO,OAAO,EAAE,KAAK,GAAG;AACtC;;;GCnEaC,IAAO,CAAC;AAAA,EACnB,UAAAC;AAAA,EACA,MAAAC,IAAO;AAAA,EACP,IAAIC,IAAY;AAAA,EAChB,OAAAC,IAAQ;AAAA,EACR,QAAAC,IAAS;AAAA,EACT,WAAAC;AAAA,EACA,WAAAC;AAAA,EACA,GAAGC;AACL,MAEI,gBAAAC;AAAA,EAACN;AAAA,EAAA;AAAA,IACC,WAAWL,EAAQY,EAAO,MAAMH,CAAS;AAAA,IACzC,cAAYH;AAAA,IACZ,aAAWF;AAAA,IACX,eAAaG;AAAA,IACb,kBAAgBC;AAAA,IACf,GAAGE;AAAA,IAEH,UAAAP;AAAA,EAAA;AAAA;"}
package/dist/media.css ADDED
@@ -0,0 +1,10 @@
1
+ @custom-media --mediaDesktop (max-width: 2080px);
2
+ @custom-media --mediaLaptop (max-width: 1680px);
3
+ @custom-media --mediaTablet (max-width: 1040px);
4
+ @custom-media --mediaMobile (max-width: 696px);
5
+ @custom-media --mediaMobileLS (max-height: 696px);
6
+ @custom-media --mediaMobileS (max-width: 400px);
7
+ @custom-media --mediaUseMotion (prefers-reduced-motion: no-preference);
8
+ @custom-media --mediaReduceMotion (prefers-reduced-motion: reduce);
9
+ @custom-media --mediaTouch (pointer: coarse);
10
+ @custom-media --mediaNoTouch (pointer: fine);
package/dist/model.cjs ADDED
@@ -0,0 +1,2 @@
1
+ "use client";"use strict";
2
+ //# sourceMappingURL=model.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
package/dist/model.js ADDED
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
package/dist/reset.css ADDED
@@ -0,0 +1,76 @@
1
+ :root {
2
+ color-scheme: dark light;
3
+ font-synthesis: none;
4
+ text-rendering: optimizeLegibility;
5
+ -moz-osx-font-smoothing: grayscale;
6
+ -webkit-font-smoothing: antialiased;
7
+ }
8
+
9
+ :where(html, body) {
10
+ min-height: 100%;
11
+ width: 100%;
12
+ }
13
+
14
+ *,
15
+ *::before,
16
+ *::after {
17
+ box-sizing: border-box;
18
+ }
19
+
20
+ * {
21
+ margin: 0;
22
+ }
23
+
24
+ :where(input, textarea, select, button) {
25
+ font: inherit;
26
+ color: inherit;
27
+ border: 0;
28
+ padding: 0;
29
+ background-color: transparent;
30
+ border-radius: 0;
31
+ appearance: none;
32
+ }
33
+
34
+ :where(svg, img, picture, video, iframe, canvas) {
35
+ display: block;
36
+ }
37
+
38
+ :any-link {
39
+ text-decoration: none;
40
+ }
41
+
42
+ ul {
43
+ padding: 0;
44
+ }
45
+
46
+ body {
47
+ font-family: var(--fontStack);
48
+ font-weight: var(--fontWeightRegular);
49
+ background-color: rgb(var(--rgbBackground));
50
+ transition: background var(--durationM) ease;
51
+ color: var(--colorTextBody);
52
+ width: 100%;
53
+ overflow-x: hidden;
54
+
55
+ &:focus {
56
+ outline: none;
57
+ }
58
+ }
59
+
60
+ :where(code, pre) {
61
+ font-family: var(--monoFontStack);
62
+ }
63
+
64
+ ::selection {
65
+ background: rgb(var(--rgbAccent));
66
+ color: rgb(var(--rgbBlack));
67
+ }
68
+
69
+ :focus {
70
+ outline: 4px solid rgb(var(--rgbText));
71
+ outline-offset: 4px;
72
+ }
73
+
74
+ :focus:not(:focus-visible) {
75
+ outline: none;
76
+ }
@@ -0,0 +1 @@
1
+ @keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes reveal{0%{transform:scale3d(0,1,1);transform-origin:left}50%{transform:scaleZ(1);transform-origin:left}51%{transform:scaleZ(1);transform-origin:right}to{transform:scale3d(0,1,1);transform-origin:right}}._text_18l38_1{line-height:var(--lineHeightBody);color:var(--colorTextBody)}._text_18l38_1[data-size=s]{font-size:var(--fontSizeBodyS)}._text_18l38_1[data-size=m]{font-size:var(--fontSizeBodyM)}._text_18l38_1[data-size=l]{font-size:var(--fontSizeBodyL)}._text_18l38_1[data-size=xl]{font-size:var(--fontSizeBodyXL)}._text_18l38_1[data-align=auto]{text-align:inherit}._text_18l38_1[data-align=start]{text-align:start}._text_18l38_1[data-align=center]{text-align:center}._text_18l38_1[data-weight=auto]{font-weight:inherit}._text_18l38_1[data-weight=regular]{font-weight:var(--fontWeightRegular)}._text_18l38_1[data-weight=medium]{font-weight:var(--fontWeightMedium)}._text_18l38_1[data-weight=bold]{font-weight:var(--fontWeightBold)}._text_18l38_1[data-secondary=true]{color:var(--colorTextLight)}
@@ -0,0 +1,46 @@
1
+ import { CSSProperties } from 'react';
2
+ /**
3
+ * Media query breakpoints
4
+ */
5
+ export declare const media: {
6
+ readonly desktop: 2080;
7
+ readonly laptop: 1680;
8
+ readonly tablet: 1040;
9
+ readonly mobile: 696;
10
+ readonly mobileS: 400;
11
+ };
12
+ /**
13
+ * Convert a px string to a number
14
+ */
15
+ export declare const pxToNum: (px: string) => number;
16
+ /**
17
+ * Convert a number to a px string
18
+ */
19
+ export declare const numToPx: (num: number) => string;
20
+ /**
21
+ * Convert pixel values to rem for a11y
22
+ */
23
+ export declare const pxToRem: (px: number) => string;
24
+ /**
25
+ * Convert ms token values to a raw numbers for ReactTransitionGroup
26
+ * Transition delay props
27
+ */
28
+ export declare const msToNum: (msString: string) => number;
29
+ /**
30
+ * Convert a number to an ms string
31
+ */
32
+ export declare const numToMs: (num: number) => string;
33
+ /**
34
+ * Convert an rgb theme property (e.g. rgbBlack: '0 0 0')
35
+ * to values that can be spread into a ThreeJS Color class
36
+ */
37
+ export declare const rgbToThreeColor: (rgb: string | undefined) => number[];
38
+ /**
39
+ * Convert a JS object into `--` prefixed css custom properties.
40
+ * Optionally pass a second param for normal styles
41
+ */
42
+ export declare function cssProps(props: Record<string, string | number>, style?: CSSProperties): Record<string, string | number> & CSSProperties;
43
+ /**
44
+ * Concatenate classNames together
45
+ */
46
+ export declare function classes(...args: (string | false | undefined | null)[]): string;
package/package.json ADDED
@@ -0,0 +1,111 @@
1
+ {
2
+ "name": "refract-ui",
3
+ "version": "0.0.0",
4
+ "description": "Themeable React component library — primitives, form controls, and Three.js device/carousel components.",
5
+ "homepage": "https://storybook.parammehta.com",
6
+ "repository": "https://github.com/parammehta/refract-ui.git",
7
+ "author": "Param Mehta <param.mehta95@gmail.com>",
8
+ "license": "MIT",
9
+ "type": "module",
10
+ "sideEffects": [
11
+ "**/*.css"
12
+ ],
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "publishConfig": {
17
+ "access": "public",
18
+ "provenance": true
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js",
24
+ "require": "./dist/index.cjs"
25
+ },
26
+ "./model": {
27
+ "types": "./dist/model.d.ts",
28
+ "import": "./dist/model.js",
29
+ "require": "./dist/model.cjs"
30
+ },
31
+ "./carousel": {
32
+ "types": "./dist/carousel.d.ts",
33
+ "import": "./dist/carousel.js",
34
+ "require": "./dist/carousel.cjs"
35
+ },
36
+ "./styles.css": "./dist/styles.css",
37
+ "./tokens.css": "./dist/tokens.css",
38
+ "./reset.css": "./dist/reset.css",
39
+ "./media.css": "./dist/media.css",
40
+ "./assets/*": "./dist/assets/*",
41
+ "./package.json": "./package.json"
42
+ },
43
+ "peerDependencies": {
44
+ "framer-motion": "^7 || ^10 || ^11 || ^12",
45
+ "react": "^18.3.1 || ^19",
46
+ "react-dom": "^18.3.1 || ^19",
47
+ "three": "^0.185",
48
+ "three-stdlib": "^2"
49
+ },
50
+ "peerDependenciesMeta": {
51
+ "three": {
52
+ "optional": true
53
+ },
54
+ "three-stdlib": {
55
+ "optional": true
56
+ }
57
+ },
58
+ "devDependencies": {
59
+ "@eslint/js": "^9.39.5",
60
+ "@storybook/addon-a11y": "^10.5.5",
61
+ "@storybook/addon-docs": "^10.5.5",
62
+ "@storybook/react-vite": "^10.5.5",
63
+ "@testing-library/jest-dom": "^7.0.0",
64
+ "@testing-library/react": "^16.3.2",
65
+ "@types/node": "^20.19.43",
66
+ "@types/react": "~18.3",
67
+ "@types/react-dom": "~18.3",
68
+ "@types/three": "^0.185.3",
69
+ "@vitejs/plugin-react": "^4.3.4",
70
+ "eslint": "^9.39.5",
71
+ "eslint-plugin-react-hooks": "^5.1.0",
72
+ "eslint-plugin-storybook": "^10.5.5",
73
+ "framer-motion": "^7.10.3",
74
+ "fs-extra": "^10.1.0",
75
+ "jsdom": "^25.0.1",
76
+ "postcss": "^8.5.24",
77
+ "postcss-flexbugs-fixes": "^5.0.2",
78
+ "postcss-preset-env": "^7.8.3",
79
+ "prettier": "^3.9.6",
80
+ "react": "^18.3.1",
81
+ "react-dom": "^18.3.1",
82
+ "rollup-plugin-preserve-directives": "^0.4.0",
83
+ "storybook": "^10.5.5",
84
+ "stylelint": "^17.14.1",
85
+ "stylelint-config-css-modules": "^4.6.0",
86
+ "stylelint-config-standard": "^40.0.0",
87
+ "three": "^0.185.1",
88
+ "three-stdlib": "^2.36.1",
89
+ "typescript": "^5.7.3",
90
+ "typescript-eslint": "^8.65.0",
91
+ "vite": "^6.0.7",
92
+ "vite-plugin-dts": "^4.4.0",
93
+ "vite-plugin-svgr": "^4.3.0",
94
+ "vitest": "^3.2.4"
95
+ },
96
+ "scripts": {
97
+ "dev": "storybook dev -p 9009 -h localhost",
98
+ "storybook": "storybook dev -p 9009 -h localhost",
99
+ "build": "vite build && node scripts/copy-static-css.cjs",
100
+ "build:storybook": "storybook build -o build-storybook && node scripts/draco-storybook.cjs && node scripts/models-storybook.cjs",
101
+ "lint": "eslint .",
102
+ "stylelint": "stylelint \"src/**/*.css\"",
103
+ "typecheck": "tsc --noEmit",
104
+ "test": "vitest run",
105
+ "test:watch": "vitest",
106
+ "deploy:storybook": "aws s3 sync --delete --exclude '.DS_Store' build-storybook s3://parammehta-portfolio-storybook && node scripts/invalidate-cloudfront-storybook.cjs"
107
+ },
108
+ "engines": {
109
+ "node": ">=20.9.0"
110
+ }
111
+ }