, DataAttributes {
18 | id?: string
19 | role?: React.AriaRole
20 | tabIndex?: number
21 | style?: React.CSSProperties
22 | }
23 |
24 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/shared/types.ts
25 | type PropGetter, R = DOMAttributes> = (
26 | props?: Merge,
27 | ref?: React.Ref,
28 | ) => R & React.RefAttributes
29 |
30 | export interface FormControlOptions {
31 | /**
32 | * If `true`, the form control will be required. This has 2 side effects:
33 | * - The `FormLabel` will show a required indicator
34 | * - The form element (e.g, Input) will have `aria-required` set to `true`
35 | *
36 | * @default false
37 | */
38 | isRequired?: boolean
39 | /**
40 | * If `true`, the form control will be disabled. This has 2 side effects:
41 | * - The `FormLabel` will have `data-disabled` attribute
42 | * - The form element (e.g, Input) will be disabled
43 | *
44 | * @default false
45 | */
46 | isDisabled?: boolean
47 | /**
48 | * If `true`, the form control will be invalid. This has 2 side effects:
49 | * - The `FormLabel` and `FormErrorIcon` will have `data-invalid` set to `true`
50 | * - The form element (e.g, Input) will have `aria-invalid` set to `true`
51 | *
52 | * @default false
53 | */
54 | isInvalid?: boolean
55 | /**
56 | * If `true`, the form control will be readonly
57 | *
58 | * @default false
59 | */
60 | isReadOnly?: boolean
61 | }
62 |
63 | interface FormControlContext extends FormControlOptions {
64 | /**
65 | * The label text used to inform users as to what information is
66 | * requested for a text field.
67 | */
68 | label?: string
69 | /**
70 | * The custom `id` to use for the form control. This is passed directly to the form element (e.g, Input).
71 | * - The form element (e.g. Input) gets the `id`
72 | * - The form label id: `form-label-${id}`
73 | * - The form error text id: `form-error-text-${id}`
74 | * - The form helper text id: `form-helper-text-${id}`
75 | */
76 | id?: string
77 | }
78 |
79 | export type FormControlProviderContext = Omit, 'getRootProps' | 'htmlProps'>
80 |
81 | export const [FormControlProvider, useFormControlContext] = createContext({
82 | strict: false,
83 | name: 'FormControlContext',
84 | })
85 |
86 | export function useFormControlProvider(props: FormControlContext) {
87 | const { id: idProp, isRequired, isInvalid, isDisabled, isReadOnly, ...htmlProps } = props
88 |
89 | // Generate all the required ids
90 | const uuid = useId()
91 | const id = idProp || `field-${uuid}`
92 |
93 | const labelId = `${id}-label`
94 | const feedbackId = `${id}-feedback`
95 | const helpTextId = `${id}-helptext`
96 |
97 | /**
98 | * Track whether the `FormErrorMessage` has been rendered.
99 | * We use this to append its id the `aria-describedby` of the `input`.
100 | */
101 | const [hasFeedbackText, setHasFeedbackText] = useState(false)
102 |
103 | /**
104 | * Track whether the `FormHelperText` has been rendered.
105 | * We use this to append its id the `aria-describedby` of the `input`.
106 | */
107 | const [hasHelpText, setHasHelpText] = useState(false)
108 |
109 | // Track whether the form element (e.g, `input`) has focus.
110 | const [isFocused, setFocus] = useState(false)
111 |
112 | const getHelpTextProps = useCallback(
113 | (props = {}, forwardedRef = null) => ({
114 | id: helpTextId,
115 | ...props,
116 | /**
117 | * Notify the field context when the help text is rendered on screen,
118 | * so we can apply the correct `aria-describedby` to the field (e.g. input, textarea).
119 | */
120 | ref: mergeRefs(forwardedRef, (node) => {
121 | if (!node) return
122 | setHasHelpText(true)
123 | }),
124 | }),
125 | [helpTextId],
126 | )
127 |
128 | const getLabelProps = useCallback(
129 | (props = {}, forwardedRef = null) => ({
130 | ...props,
131 | ref: forwardedRef,
132 | 'data-focus': dataAttr(isFocused),
133 | 'data-disabled': dataAttr(isDisabled),
134 | 'data-invalid': dataAttr(isInvalid),
135 | 'data-readonly': dataAttr(isReadOnly),
136 | id: props.id !== undefined ? props.id : labelId,
137 | htmlFor: props.htmlFor !== undefined ? props.htmlFor : id,
138 | }),
139 | [id, isDisabled, isFocused, isInvalid, isReadOnly, labelId],
140 | )
141 |
142 | const getErrorMessageProps = useCallback(
143 | (props = {}, forwardedRef = null) => ({
144 | id: feedbackId,
145 | ...props,
146 | /**
147 | * Notify the field context when the error message is rendered on screen,
148 | * so we can apply the correct `aria-describedby` to the field (e.g. input, textarea).
149 | */
150 | ref: mergeRefs(forwardedRef, (node) => {
151 | if (!node) return
152 | setHasFeedbackText(true)
153 | }),
154 | 'aria-live': 'polite',
155 | }),
156 | [feedbackId],
157 | )
158 |
159 | const getRootProps = useCallback(
160 | (props = {}, forwardedRef = null) => ({
161 | ...props,
162 | ...htmlProps,
163 | ref: forwardedRef,
164 | role: 'group',
165 | }),
166 | [htmlProps],
167 | )
168 |
169 | const getRequiredIndicatorProps = useCallback(
170 | (props = {}, forwardedRef = null) => ({
171 | ...props,
172 | ref: forwardedRef,
173 | role: 'presentation',
174 | 'aria-hidden': true,
175 | children: props.children || '*',
176 | }),
177 | [],
178 | )
179 |
180 | return {
181 | isRequired: !!isRequired,
182 | isInvalid: !!isInvalid,
183 | isReadOnly: !!isReadOnly,
184 | isDisabled: !!isDisabled,
185 | isFocused: !!isFocused,
186 | onFocus: () => setFocus(true),
187 | onBlur: () => setFocus(false),
188 | hasFeedbackText,
189 | setHasFeedbackText,
190 | hasHelpText,
191 | setHasHelpText,
192 | id,
193 | labelId,
194 | feedbackId,
195 | helpTextId,
196 | htmlProps,
197 | getHelpTextProps,
198 | getErrorMessageProps,
199 | getRootProps,
200 | getLabelProps,
201 | getRequiredIndicatorProps,
202 | }
203 | }
204 |
205 | export type FormContextGetterProps = Extract
206 |
--------------------------------------------------------------------------------
/packages/components/src/ui/form-control.tsx:
--------------------------------------------------------------------------------
1 | import { Assign } from "@crepe-ui/styled-system/types";
2 | import { forwardRef } from "react";
3 | import { HTMLStyledProps, styled } from "@crepe-ui/styled-system/jsx";
4 | import {
5 | formControl,
6 | type FormControlVariantProps,
7 | } from "@crepe-ui/styled-system/recipes";
8 | import { ComponentProps } from "@crepe-ui/styled-system/types";
9 | import { createFormContextElement } from "./create-form-element";
10 | import { createStyleContext } from "./create-style-context";
11 | import {
12 | FormControlOptions,
13 | FormControlProvider,
14 | useFormControlContext,
15 | useFormControlProvider,
16 | } from "./form-control-context";
17 | import { Icon } from "./icon";
18 |
19 | const { withProvider, withContext } = createStyleContext(formControl);
20 |
21 | // export * from '@ark-ui/react/formControl';
22 | interface StyleProps extends HTMLStyledProps<"div"> {}
23 | interface JsxProps extends Assign, StyleProps> {}
24 |
25 | interface FormControlContext extends FormControlOptions {
26 | /**
27 | * The label text used to inform users as to what information is
28 | * requested for a text field.
29 | */
30 | label?: string;
31 | /**
32 | * The custom `id` to use for the form control. This is passed directly to the form element (e.g, Input).
33 | * - The form element (e.g. Input) gets the `id`
34 | * - The form label id: `form-label-${id}`
35 | * - The form error text id: `form-error-text-${id}`
36 | * - The form helper text id: `form-helper-text-${id}`
37 | */
38 | id?: string;
39 | }
40 |
41 | export interface FormControlProps
42 | extends Omit,
43 | FormControlVariantProps,
44 | FormControlContext {}
45 |
46 | const StyledContainer = withProvider(styled("div"), "container");
47 |
48 | /**
49 | * FormControl provides context such as
50 | * `isInvalid`, `isDisabled`, and `isRequired` to form elements.
51 | *
52 | * This is commonly used in form elements such as `input`,
53 | * `select`, `textarea`, etc.
54 | *
55 | * @see Docs https://chakra-ui.com/docs/components/form-control
56 | */
57 | const FormControlContainer = forwardRef<"div", FormControlProps>(
58 | function FormControl(props, ref) {
59 | const {
60 | getRootProps,
61 | htmlProps: _,
62 | ...context
63 | } = useFormControlProvider(props);
64 |
65 | return (
66 |
67 |
68 |
69 | );
70 | }
71 | );
72 | FormControlContainer.displayName = "FormControl";
73 |
74 | //
75 |
76 | export interface FormLabelProps extends HTMLStyledProps<"label"> {}
77 | const StyledLabel = withContext(styled("label"), "label");
78 |
79 | // Styles taken from `packages/preset-chakra/src/recipes/form-control.recipe.ts`
80 | // because there's no recipe extension yet
81 | // TODO recipe extension
82 | export const FormLabel = styled(StyledLabel, {
83 | base: {
84 | display: "block",
85 | textAlign: "start",
86 | //
87 | fontSize: "md",
88 | marginEnd: "3",
89 | mb: "2",
90 | fontWeight: "medium",
91 | transitionProperty: "common",
92 | transitionDuration: "normal",
93 | opacity: 1,
94 | _disabled: {
95 | opacity: 0.4,
96 | },
97 | },
98 | });
99 |
100 | /**
101 | * Used to enhance the usability of form controls.
102 | *
103 | * It is used to inform users as to what information
104 | * is requested for a form field.
105 | *
106 | * ♿️ Accessibility: Every form field should have a form label.
107 | */
108 | const FormControlLabel = createFormContextElement(
109 | StyledLabel,
110 | "FormLabel",
111 | "getLabelProps"
112 | );
113 |
114 | //
115 |
116 | export interface FormRequiredIndicatorProps extends HTMLStyledProps<"span"> {}
117 | const StyledRequiredIndicator = withContext(
118 | styled("span"),
119 | "required-indicator"
120 | );
121 |
122 | /**
123 | * Used to enhance the usability of form controls.
124 | *
125 | * It is used to inform users as to what information
126 | * is requested for a form field.
127 | *
128 | * ♿️ Accessibility: Every form field should have a form requiredIndicator.
129 | */
130 | const FormRequiredIndicator = createFormContextElement(
131 | StyledRequiredIndicator,
132 | "FormRequiredIndicator",
133 | "getRequiredIndicatorProps",
134 | (field) => field?.isRequired
135 | );
136 |
137 | //
138 |
139 | export interface FormHelperTextProps extends HTMLStyledProps<"div"> {}
140 | const StyledHelperText = withContext(styled("div"), "helper");
141 |
142 | /**
143 | * FormHelperText
144 | *
145 | * Assistive component that conveys additional guidance
146 | * about the field, such as how it will be used and what
147 | * types in values should be provided.
148 | */
149 | const FormHelperText = createFormContextElement(
150 | StyledHelperText,
151 | "FormHelperText",
152 | "getHelpTextProps"
153 | );
154 | // const FormHelperText = StyledHelperText;
155 |
156 | //
157 |
158 | export interface FormErrorProps extends HTMLStyledProps<"div"> {}
159 | const StyledError = withContext(styled("div"), "error");
160 |
161 | /**
162 | * FormError
163 | *
164 | * Assistive component that conveys additional guidance
165 | * about the field, such as how it will be used and what
166 | * types in values should be provided.
167 | */
168 | const FormError = createFormContextElement(
169 | StyledError,
170 | "FormError",
171 | "getErrorMessageProps",
172 | (field) => field?.isInvalid
173 | );
174 |
175 | //
176 |
177 | const ErrorIcon = () => (
178 |
182 | );
183 |
184 | export interface FormErrorIconProps extends HTMLStyledProps {}
185 | const StyledErrorIcon = withContext(Icon, "error-icon");
186 |
187 | /**
188 | * FormErrorIcon
189 | *
190 | * Assistive component that conveys additional guidance
191 | * about the field, such as how it will be used and what
192 | * types in values should be provided.
193 | */
194 | const FormErrorIcon = forwardRef(
195 | function FormErrorIcon(props, ref) {
196 | const field = useFormControlContext();
197 | if (!field?.isInvalid) return null;
198 |
199 | return (
200 |
201 |
202 |
203 | );
204 | }
205 | );
206 | FormErrorIcon.displayName = "FormErrorIcon";
207 |
208 | //
209 |
210 | export const FormControl = Object.assign(FormControlContainer, {
211 | Container: FormControlContainer,
212 | Label: FormControlLabel,
213 | Helper: FormHelperText,
214 | RequiredIndicator: FormRequiredIndicator,
215 | Error: FormError,
216 | ErrorIcon: FormErrorIcon,
217 | });
218 |
--------------------------------------------------------------------------------
/packages/components/src/ui/heading.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from "@crepe-ui/styled-system/jsx";
2 | import { heading } from "@crepe-ui/styled-system/recipes";
3 |
4 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/heading.ts
5 |
6 | export const Heading = styled("h2", heading);
7 |
--------------------------------------------------------------------------------
/packages/components/src/ui/icon.tsx:
--------------------------------------------------------------------------------
1 | import { ark } from "@ark-ui/react";
2 | import { styled } from "@crepe-ui/styled-system/jsx";
3 | import { icon } from "@crepe-ui/styled-system/recipes";
4 | import { forwardRef } from "react";
5 | import { Button, ButtonProps } from "./button";
6 |
7 | export const Icon = styled(ark.svg, icon, {
8 | defaultProps: {
9 | // TODO this one seems to cause issues for some icons
10 | // viewBox: '0 0 24 24',
11 | role: "presentation",
12 | "aria-hidden": true,
13 | focusable: false,
14 | },
15 | });
16 |
17 | export const IconButton = forwardRef(
18 | (props, ref) => {
19 | return ;
20 | }
21 | );
22 |
--------------------------------------------------------------------------------
/packages/components/src/ui/image.ts:
--------------------------------------------------------------------------------
1 | import { styled } from '@crepe-ui/styled-system/jsx'
2 |
3 | export const Image = styled.img
4 |
--------------------------------------------------------------------------------
/packages/components/src/ui/input-addon.tsx:
--------------------------------------------------------------------------------
1 | import { cva, cx } from "@crepe-ui/styled-system/css";
2 | import { styled } from "@crepe-ui/styled-system/jsx";
3 | import {
4 | HTMLStyledProps,
5 | RecipeVariantProps,
6 | } from "@crepe-ui/styled-system/types";
7 | import { forwardRef } from "react";
8 |
9 | const styles = cva({
10 | base: {
11 | flex: "0 0 auto",
12 | width: "auto",
13 | display: "flex",
14 | alignItems: "center",
15 | whiteSpace: "nowrap",
16 | },
17 | variants: {
18 | placement: {
19 | left: {
20 | marginEnd: "-1px",
21 | borderEndRadius: 0,
22 | borderEndColor: "transparent",
23 | },
24 | right: {
25 | marginStart: "-1px",
26 | borderStartRadius: 0,
27 | borderStartColor: "transparent",
28 | },
29 | },
30 | },
31 | });
32 |
33 | export interface InputAddonProps
34 | extends HTMLStyledProps<"div">,
35 | NonNullable> {}
36 |
37 | export const InputAddon = forwardRef(
38 | function InputAddon(props, ref) {
39 | const { placement = "left", className, ...rest } = props;
40 |
41 | return (
42 |
48 | );
49 | }
50 | );
51 |
52 | InputAddon.displayName = "InputAddon";
53 |
--------------------------------------------------------------------------------
/packages/components/src/ui/input.tsx:
--------------------------------------------------------------------------------
1 | import { Assign } from "@crepe-ui/styled-system/types";
2 | import { createStyleContext } from "./create-style-context";
3 | import { InputAddon } from "./input-addon";
4 | import { useFormControl } from "./use-form-control";
5 | import { ComponentProps, forwardRef } from "react";
6 | import { FormControlOptions } from "./form-control-context";
7 | import { styled } from "@crepe-ui/styled-system/jsx";
8 | import { input, InputVariantProps } from "@crepe-ui/styled-system/recipes";
9 | import { HTMLStyledProps } from "@crepe-ui/styled-system/types";
10 |
11 | const { withProvider, withContext } = createStyleContext(input);
12 |
13 | // export * from '@ark-ui/react/input';
14 | interface StyleProps extends HTMLStyledProps<"input"> {}
15 | interface JsxProps extends Assign, StyleProps> {}
16 |
17 | export interface InputProps
18 | extends Omit,
19 | InputVariantProps,
20 | FormControlOptions {}
21 |
22 | const StyledInputRoot = withProvider(styled("input"), "field");
23 | const InputRoot = forwardRef<"span", InputProps>(function FormErrorIcon(
24 | props,
25 | ref
26 | ) {
27 | const fieldProps = useFormControl(props);
28 |
29 | return ;
30 | });
31 | InputRoot.displayName = "Input";
32 |
33 | const InputGroup = withProvider(styled("div"), "group");
34 |
35 | export const Input = Object.assign(InputRoot, {
36 | Root: InputRoot,
37 | Group: InputGroup,
38 | Field: withContext(styled("input"), "field"),
39 | Addon: withContext(InputAddon, "addon"),
40 | });
41 |
--------------------------------------------------------------------------------
/packages/components/src/ui/kbd.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from "@crepe-ui/styled-system/jsx";
2 | import { kbd } from "@crepe-ui/styled-system/recipes";
3 |
4 | export const Kbd = styled("span", kbd);
5 |
--------------------------------------------------------------------------------
/packages/components/src/ui/link.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentPropsWithoutRef, forwardRef } from "react";
2 | import { styled } from "@crepe-ui/styled-system/jsx";
3 | import { CheckboxVariantProps, link } from "@crepe-ui/styled-system/recipes";
4 |
5 | const LinkRoot = styled("a", link);
6 |
7 | interface StyleProps extends ComponentPropsWithoutRef {}
8 |
9 | export interface LinkProps extends StyleProps, CheckboxVariantProps {
10 | /**
11 | * If `true`, the link will open in new tab
12 | *
13 | * @default false
14 | */
15 | isExternal?: boolean;
16 | }
17 |
18 | /**
19 | * Links are accessible elements used primarily for navigation.
20 | *
21 | * It integrates well with other routing libraries like
22 | * React Router, Reach Router and Next.js Link.
23 | *
24 | * @example
25 | *
26 | * ```jsx
27 | * Home
28 | * ```
29 | *
30 | * @see Docs https://chakra-ui.com/link
31 | */
32 | export const Link = forwardRef(function Link(
33 | { isExternal, ...props },
34 | ref
35 | ) {
36 | return (
37 |
43 | );
44 | });
45 |
46 | Link.displayName = "Link";
47 |
--------------------------------------------------------------------------------
/packages/components/src/ui/list.tsx:
--------------------------------------------------------------------------------
1 | import { sva } from "@crepe-ui/styled-system/css";
2 | import { styled } from "@crepe-ui/styled-system/jsx";
3 | import { createStyleContext } from "./create-style-context";
4 |
5 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/list.ts
6 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/layout/list.tsx
7 |
8 | const listRecipe = sva({
9 | slots: ["ul", "ol", "icon", "item"],
10 | base: {
11 | ul: {
12 | listStyleType: "initial",
13 | marginStart: "1em",
14 | },
15 | ol: {
16 | listStyleType: "decimal",
17 | marginStart: "1em",
18 | },
19 | icon: {
20 | marginEnd: "2",
21 | display: "inline",
22 | verticalAlign: "text-bottom",
23 | },
24 | },
25 | });
26 |
27 | const { withProvider, withContext } = createStyleContext(listRecipe);
28 |
29 | const UnorderedList = withProvider(styled.ul, "ul");
30 | const OrderedList = withProvider(styled.ul, "ol");
31 |
32 | const ListItem = withContext(styled.li, "item");
33 | const ListIcon = withContext(styled.li, "icon");
34 |
35 | export const List = Object.assign(UnorderedList, {
36 | Unordered: UnorderedList,
37 | Ordered: OrderedList,
38 | Item: ListItem,
39 | Icon: ListIcon,
40 | });
41 |
--------------------------------------------------------------------------------
/packages/components/src/ui/modal.tsx:
--------------------------------------------------------------------------------
1 | import * as Ark from "@ark-ui/react/dialog";
2 | import { HTMLStyledProps, styled } from "@crepe-ui/styled-system/jsx";
3 | import {
4 | modal,
5 | type ModalVariantProps,
6 | closeButton,
7 | } from "@crepe-ui/styled-system/recipes";
8 | import { createStyleContext } from "./create-style-context";
9 | import { Assign } from "@crepe-ui/styled-system/types";
10 | import { ForwardRefExoticComponent, forwardRef } from "react";
11 | import { DialogDescriptionProps } from "@ark-ui/react/dialog";
12 |
13 | const { withProvider, withContext } = createStyleContext(modal);
14 |
15 | // export * from '@ark-ui/react/dialog';
16 | // export type ModalProps = Ark.DialogProps & ModalVariantProps;
17 | interface StyleProps extends HTMLStyledProps {}
18 | interface JsxProps extends Assign {}
19 |
20 | export interface ModalProps extends JsxProps, ModalVariantProps {}
21 |
22 | const DialogRoot = withProvider(styled(Ark.Dialog.Root), "root");
23 | const DialogBackdrop = withContext(styled(Ark.Dialog.Backdrop), "backdrop");
24 | const DialogCloseTrigger = withContext(
25 | // TODO recipe extension
26 | styled(Ark.Dialog.CloseTrigger, closeButton),
27 | "closeTrigger"
28 | );
29 | const DialogContainer = withContext(styled(Ark.Dialog.Positioner), "container");
30 | const DialogContent = withContext(styled(Ark.Dialog.Content), "content");
31 |
32 | const OverrideDialogDescription = forwardRef((props, ref) => {
33 | // @ts-expect-error Chakra-ui ModalBody is a div, Ark-UI DialogDescription is a p
34 | // if we use p, we can't put divs etc in there
35 | return ;
36 | });
37 | const DialogDescription = withContext(
38 | styled(OverrideDialogDescription) as ForwardRefExoticComponent<
39 | HTMLStyledProps<"div"> & DialogDescriptionProps
40 | >,
41 | "description"
42 | );
43 | const DialogTitle = withContext(styled(Ark.Dialog.Title), "title");
44 | const DialogTrigger = withContext(styled(Ark.Dialog.Trigger), "trigger");
45 | const DialogFooter = withContext(styled("div"), "footer");
46 |
47 | export const Modal = Object.assign(DialogRoot, {
48 | Root: DialogRoot,
49 | Overlay: DialogBackdrop,
50 | Close: DialogCloseTrigger,
51 | Container: DialogContainer,
52 | Content: DialogContent,
53 | Body: DialogDescription,
54 | Header: DialogTitle,
55 | Trigger: DialogTrigger,
56 | Footer: DialogFooter,
57 | });
58 |
59 | export const ModalCloseButton = styled("button", closeButton);
60 |
--------------------------------------------------------------------------------
/packages/components/src/ui/popover.tsx:
--------------------------------------------------------------------------------
1 | import * as Ark from "@ark-ui/react/popover";
2 | import { Assign } from "@crepe-ui/styled-system/types";
3 | import { HTMLStyledProps, styled } from "@crepe-ui/styled-system/jsx";
4 | import { popover } from "@crepe-ui/styled-system/recipes";
5 | import { createStyleContext } from "./create-style-context";
6 |
7 | const { withProvider, withContext } = createStyleContext(popover);
8 |
9 | // export * from '@ark-ui/react/popover'
10 | interface StyleProps extends HTMLStyledProps {}
11 | interface JsxProps extends Assign {}
12 |
13 | export interface PopoverProps extends JsxProps {}
14 |
15 | const PopoverRoot = withProvider(styled(Ark.Popover.Root));
16 | const PopoverAnchor = withContext(styled(Ark.Popover.Anchor), "anchor");
17 | const PopoverArrow = withContext(styled(Ark.Popover.Arrow), "arrow");
18 | const PopoverArrowTip = withContext(styled(Ark.Popover.ArrowTip), "arrowTip");
19 | const PopoverCloseTrigger = withContext(
20 | styled(Ark.Popover.CloseTrigger),
21 | "closeTrigger"
22 | );
23 | const PopoverContent = withContext(styled(Ark.Popover.Content), "content");
24 | const PopoverDescription = withContext(
25 | styled(Ark.Popover.Description),
26 | "description"
27 | );
28 | const PopoverFooter = withContext(styled("div"), "footer");
29 | const PopoverPositioner = withContext(
30 | styled(Ark.Popover.Positioner),
31 | "positioner"
32 | );
33 | const PopoverTitle = withContext(styled(Ark.Popover.Title), "title");
34 | const PopoverTrigger = withContext(styled(Ark.Popover.Trigger), "trigger");
35 |
36 | export const Popover = Object.assign(PopoverRoot, {
37 | Root: PopoverRoot,
38 | Anchor: PopoverAnchor,
39 | Arrow: PopoverArrow,
40 | ArrowTip: PopoverArrowTip,
41 | CloseTrigger: PopoverCloseTrigger,
42 | Content: PopoverContent,
43 | Description: PopoverDescription,
44 | Footer: PopoverFooter,
45 | Positioner: PopoverPositioner,
46 | Title: PopoverTitle,
47 | Trigger: PopoverTrigger,
48 | });
49 |
--------------------------------------------------------------------------------
/packages/components/src/ui/portal.tsx:
--------------------------------------------------------------------------------
1 | // https://github.com/chakra-ui/zag/blob/1ec8547a40c5c570edd77c835dfb1d7ada37b5b7/packages/frameworks/react/src/portal.ts#L10
2 |
3 | import { createElement, useReducer, useRef } from "react";
4 | import { createPortal } from "react-dom";
5 |
6 | import { useEffect, useLayoutEffect } from "react";
7 |
8 | const useSafeLayoutEffect =
9 | typeof document !== "undefined" ? useLayoutEffect : useEffect;
10 |
11 | export interface PortalProps {
12 | children: React.ReactNode;
13 | target?: React.RefObject;
14 | tag?: string;
15 | host?: HTMLElement | null;
16 | }
17 |
18 | export function Portal(props: PortalProps): JSX.Element {
19 | const { children, target, tag, host: hostProp } = props;
20 | const node = useRef(null);
21 | const portalNode = useRef(null);
22 | const [, forceUpdate] = useReducer((s) => s + 1, 0);
23 |
24 | useSafeLayoutEffect(() => {
25 | if (!node.current) return;
26 | const doc = node.current.ownerDocument;
27 | const host = hostProp ?? doc.body;
28 | portalNode.current = doc.createElement(tag ?? "crepe-ui-portal");
29 | host.appendChild(portalNode.current);
30 |
31 | forceUpdate();
32 |
33 | return () => {
34 | if (portalNode.current) {
35 | host.removeChild(portalNode.current);
36 | }
37 | };
38 | }, []);
39 |
40 | const targetNode = target?.current ?? portalNode.current;
41 |
42 | if (targetNode) {
43 | return createPortal(children as any, targetNode);
44 | }
45 |
46 | return createElement("span", { ref: node });
47 | }
48 |
--------------------------------------------------------------------------------
/packages/components/src/ui/select.tsx:
--------------------------------------------------------------------------------
1 | import * as Ark from "@ark-ui/react/select";
2 | import { HTMLStyledProps, styled } from "@crepe-ui/styled-system/jsx";
3 | import { SelectVariantProps, select } from "@crepe-ui/styled-system/recipes";
4 | import { createStyleContext } from "./create-style-context";
5 |
6 | import type { CollectionItem } from "@ark-ui/react/select";
7 | import type * as zag from "@zag-js/select";
8 | import { Optional } from "./types";
9 | import { ComponentProps, ForwardRefExoticComponent } from "react";
10 | import { Assign } from "@crepe-ui/styled-system/types";
11 | import type { HTMLArkProps } from "@ark-ui/react";
12 |
13 | const { withProvider, withContext } = createStyleContext(select);
14 |
15 | // export * from '@ark-ui/react/select';
16 | interface StyleProps extends HTMLStyledProps {}
17 | interface JsxProps
18 | extends Assign, StyleProps> {}
19 |
20 | export interface SelectProps
21 | extends JsxProps,
22 | SelectVariantProps {}
23 |
24 | // Ark-UI doesn't (yet ?) expose the UseXXXProps and we need it for tsc .d.ts
25 | // https://github.com/microsoft/TypeScript/issues/47663
26 | // https://github.com/chakra-ui/ark/blob/ba18a28ac8dae026d2489e6fb19d4064beaeb407/packages/frameworks/react/src/select/use-select.ts
27 | interface UseSelectProps
28 | extends zag.CollectionOptions,
29 | Omit, "id">, "collection"> {
30 | /**
31 | * The initial value of the select.
32 | */
33 | defaultValue?: zag.Context["value"];
34 | }
35 |
36 | interface SelectItemProps
37 | extends Assign<
38 | HTMLArkProps<"div">,
39 | {
40 | children?:
41 | | React.ReactNode
42 | | ((props: zag.ItemState) => React.ReactNode);
43 | }
44 | >,
45 | zag.ItemProps {}
46 |
47 | interface SelectItemGroupProps
48 | extends Assign, zag.ItemGroupProps> {}
49 |
50 | interface SelectItemGroupLabelProps
51 | extends Assign, zag.ItemGroupLabelProps> {}
52 |
53 | // and that means we also have to cast this one
54 | const SelectRoot = withProvider(
55 | styled(
56 | Ark.Select.Root as ForwardRefExoticComponent<
57 | ComponentProps<"div"> & UseSelectProps
58 | >
59 | ),
60 | "root"
61 | );
62 |
63 | const SelectClearTrigger = withContext(
64 | styled(Ark.Select.ClearTrigger),
65 | "clearTrigger"
66 | );
67 | const SelectContent = withContext(styled(Ark.Select.Content), "content");
68 | const SelectControl = withContext(styled(Ark.Select.Control), "control");
69 | const SelectItem = withContext(
70 | styled(
71 | Ark.Select.Item as ForwardRefExoticComponent<
72 | ComponentProps<"div"> & SelectItemProps
73 | >
74 | ),
75 | "item"
76 | );
77 | const SelectItemGroup = withContext(
78 | styled(Ark.Select.ItemGroup) as any as ForwardRefExoticComponent<
79 | ComponentProps<"div"> & SelectItemGroupProps
80 | >,
81 | "itemGroup"
82 | );
83 | const SelectItemGroupLabel = withContext(
84 | styled(Ark.Select.ItemGroupLabel) as any as ForwardRefExoticComponent<
85 | ComponentProps<"div"> & SelectItemGroupLabelProps
86 | >,
87 | "itemGroupLabel"
88 | );
89 | const SelectItemIndicator = withContext(
90 | styled(Ark.Select.ItemIndicator),
91 | "itemIndicator"
92 | );
93 | const SelectItemText = withContext(styled(Ark.Select.ItemText), "itemText");
94 | const SelectLabel = withContext(styled(Ark.Select.Label), "label");
95 | const SelectPositioner = withContext(
96 | styled(Ark.Select.Positioner),
97 | "positioner"
98 | );
99 | const SelectTrigger = withContext(styled(Ark.Select.Trigger), "trigger");
100 | const SelectValue = withContext(styled(Ark.Select.ValueText), "value");
101 |
102 | export const Select = Object.assign(SelectRoot, {
103 | Root: SelectRoot,
104 | ClearTrigger: SelectClearTrigger,
105 | Content: SelectContent,
106 | Control: SelectControl,
107 | Item: SelectItem,
108 | ItemGroup: SelectItemGroup,
109 | ItemGroupLabel: SelectItemGroupLabel,
110 | ItemIndicator: SelectItemIndicator,
111 | ItemText: SelectItemText,
112 | Label: SelectLabel,
113 | Positioner: SelectPositioner,
114 | Trigger: SelectTrigger,
115 | Value: SelectValue,
116 | });
117 |
--------------------------------------------------------------------------------
/packages/components/src/ui/skeleton.tsx:
--------------------------------------------------------------------------------
1 | import { cssVariables } from "@crepe-ui/preset-chakra/vars";
2 | import { HTMLStyledProps, styled } from "@crepe-ui/styled-system/jsx";
3 | import {
4 | SkeletonVariantProps,
5 | skeleton,
6 | } from "@crepe-ui/styled-system/recipes";
7 | import { forwardRef } from "react";
8 |
9 | export interface SkeletonProps
10 | extends SkeletonVariantProps,
11 | HTMLStyledProps<"div"> {
12 | startColor?: string;
13 | endColor?: string;
14 | }
15 |
16 | const SkeletonRoot = styled("div", skeleton);
17 |
18 | /**
19 | * `Skeleton` is used to display the loading state of some component.
20 | *
21 | * @see Docs https://chakra-ui.com/docs/components/skeleton
22 | */
23 | export const Skeleton = forwardRef(
24 | ({ children, startColor, endColor, style, ...props }, ref) => {
25 | return (
26 |
37 | {children}
38 |
39 | );
40 | }
41 | );
42 |
--------------------------------------------------------------------------------
/packages/components/src/ui/switch.tsx:
--------------------------------------------------------------------------------
1 | import * as Ark from "@ark-ui/react/switch";
2 | import { HTMLStyledProps, styled } from "@crepe-ui/styled-system/jsx";
3 | import {
4 | switchRecipe,
5 | type SwitchRecipeVariantProps,
6 | } from "@crepe-ui/styled-system/recipes";
7 | import { createStyleContext } from "./create-style-context";
8 | import { Assign } from "@crepe-ui/styled-system/types";
9 |
10 | import type * as zag from "@zag-js/switch";
11 | import { Optional } from "./types";
12 | import { ComponentProps, ForwardRefExoticComponent } from "react";
13 |
14 | const { withProvider, withContext } = createStyleContext(switchRecipe);
15 |
16 | interface StyleProps extends HTMLStyledProps<"input"> {}
17 | interface JsxProps extends Assign {}
18 |
19 | export interface SwitchProps
20 | extends Omit,
21 | SwitchRecipeVariantProps {}
22 |
23 | export interface UseSwitchProps extends Optional {
24 | /**
25 | * The initial checked state of the switch.
26 | */
27 | defaultChecked?: zag.Context["checked"];
28 | }
29 |
30 | // Ark-UI doesn't (yet ?) expose the UseXXXProps and we need it for tsc .d.ts
31 | // https://github.com/microsoft/TypeScript/issues/47663
32 | // https://github.com/chakra-ui/ark/blob/ba18a28ac8dae026d2489e6fb19d4064beaeb407/packages/frameworks/react/src/avatar/use-avatar.ts
33 | interface UseAvatarProps extends Optional {}
34 |
35 | // and that means we also have to cast this one
36 | const SwitchRoot = withProvider(
37 | styled(
38 | Ark.Switch.Root as ForwardRefExoticComponent<
39 | ComponentProps<"label"> & UseAvatarProps
40 | >
41 | ),
42 | "root"
43 | );
44 |
45 | const SwitchControl = withContext(styled(Ark.Switch.Control), "control");
46 | const SwitchLabel = withContext(styled(Ark.Switch.Label), "label");
47 | const SwitchThumb = withContext(styled(Ark.Switch.Thumb), "thumb");
48 |
49 | export const Switch = Object.assign(SwitchRoot, {
50 | Root: SwitchRoot,
51 | Control: SwitchControl,
52 | Label: SwitchLabel,
53 | Thumb: SwitchThumb,
54 | });
55 |
--------------------------------------------------------------------------------
/packages/components/src/ui/table.tsx:
--------------------------------------------------------------------------------
1 | import type { ComponentPropsWithoutRef } from "react";
2 | import { HTMLStyledProps, styled } from "@crepe-ui/styled-system/jsx";
3 | import { createStyleContext } from "./create-style-context";
4 | import { Assign } from "@crepe-ui/styled-system/types";
5 | import { table, TableVariantProps } from "@crepe-ui/styled-system/recipes";
6 |
7 | const { withProvider, withContext } = createStyleContext(table);
8 |
9 | interface StyleProps extends HTMLStyledProps<"div"> {}
10 | interface JsxProps
11 | extends Assign, StyleProps> {}
12 |
13 | export interface TableProps extends JsxProps, TableVariantProps {}
14 |
15 | const TableContainer = withProvider(styled("div"), "container");
16 | const TableRoot = withProvider(styled("table"), "table");
17 |
18 | const TableBody = withContext(styled("tbody"), "tbody");
19 | const TableCaption = withContext(styled("caption"), "caption");
20 | const TableCell = withContext(styled("td"), "td");
21 | const TableFooter = withContext(styled("tfoot"), "tfoot");
22 | const TableHead = withContext(styled("th"), "th");
23 | const TableHeader = withContext(styled("thead"), "thead");
24 | const TableRow = withContext(styled("tr"), "tr");
25 |
26 | export const Table = Object.assign(TableRoot, {
27 | Container: TableContainer,
28 | Table: TableRoot,
29 | Root: withContext(styled("table"), "table"),
30 | Body: TableBody,
31 | Caption: TableCaption,
32 | Cell: TableCell,
33 | Footer: TableFooter,
34 | Head: TableHead,
35 | Header: TableHeader,
36 | Row: TableRow,
37 | });
38 |
--------------------------------------------------------------------------------
/packages/components/src/ui/tabs.tsx:
--------------------------------------------------------------------------------
1 | import * as Ark from "@ark-ui/react/tabs";
2 | import { Assign } from "@crepe-ui/styled-system/types";
3 | import { HTMLStyledProps, styled } from "@crepe-ui/styled-system/jsx";
4 | import { TabsVariantProps, tabs } from "@crepe-ui/styled-system/recipes";
5 | import { createStyleContext } from "./create-style-context";
6 |
7 | import { ComponentProps, ForwardRefExoticComponent, ReactElement } from "react";
8 | import type * as presence from "@zag-js/presence";
9 | import type * as zag from "@zag-js/tabs";
10 | import { type Optional } from "./types";
11 | import type { HTMLArkProps } from "@ark-ui/react";
12 |
13 | const { withProvider, withContext } = createStyleContext(tabs);
14 |
15 | // export * from '@ark-ui/react/tabs';
16 | interface StyleProps extends HTMLStyledProps {}
17 | interface JsxProps extends Assign {}
18 |
19 | export interface TabsProps extends JsxProps, TabsVariantProps {}
20 |
21 | // Ark-UI doesn't (yet ?) expose the UseXXXProps and we need it for tsc .d.ts
22 | // https://github.com/microsoft/TypeScript/issues/47663
23 | // https://github.com/chakra-ui/ark/blob/ba18a28ac8dae026d2489e6fb19d4064beaeb407/packages/frameworks/react/src/tabs/use-tabs.ts
24 | interface UseTabsProps extends Optional {
25 | /**
26 | * The initial value of the tabs.
27 | */
28 | defaultValue?: zag.Context["value"];
29 | }
30 |
31 | interface UsePresenceProps extends Optional {}
32 | interface PresenceProps extends UsePresenceProps {
33 | /**
34 | * Only a single child is allowed.
35 | */
36 | children: ReactElement;
37 | /**
38 | * Whether to enable lazy mounting
39 | * @default false
40 | */
41 | lazyMount?: boolean;
42 | /**
43 | * Whether to unmount on exit.
44 | * @default false
45 | */
46 | unmountOnExit?: boolean;
47 | }
48 |
49 | interface TabPresenceProps extends PresenceProps, zag.ContentProps {}
50 | type InnerTabContentProps = HTMLArkProps<"div"> & zag.ContentProps;
51 | interface TabContentProps
52 | extends InnerTabContentProps,
53 | Omit {}
54 |
55 | interface TabTriggerProps
56 | extends Assign, zag.TriggerProps> {}
57 |
58 | const TabsRoot = withProvider(
59 | styled(
60 | Ark.Tabs.Root as ForwardRefExoticComponent<
61 | ComponentProps<"div"> & UseTabsProps
62 | >
63 | ),
64 | "root"
65 | );
66 |
67 | const TabContent = withContext(
68 | styled(
69 | Ark.Tabs.Content as ForwardRefExoticComponent<
70 | ComponentProps<"div"> & TabContentProps
71 | >
72 | ),
73 | "content"
74 | );
75 | const TabPanels = withContext(styled("div"), "panels");
76 | const TabIndicator = withContext(styled(Ark.Tabs.Indicator), "indicator");
77 | const TabList = withContext(styled(Ark.Tabs.List), "list");
78 | const TabTrigger = withContext(
79 | styled(
80 | Ark.Tabs.Trigger as ForwardRefExoticComponent<
81 | ComponentProps<"div"> & TabTriggerProps
82 | >
83 | ),
84 | "trigger"
85 | );
86 |
87 | export const Tabs = Object.assign(TabsRoot, {
88 | Root: TabsRoot,
89 | Panels: TabPanels,
90 | Content: TabContent,
91 | Indicator: TabIndicator,
92 | List: TabList,
93 | Trigger: TabTrigger,
94 | });
95 |
--------------------------------------------------------------------------------
/packages/components/src/ui/tag.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from "@crepe-ui/styled-system/jsx";
2 | import { tag } from "@crepe-ui/styled-system/recipes";
3 |
4 | export const Tag = styled("span", tag);
5 |
--------------------------------------------------------------------------------
/packages/components/src/ui/text.ts:
--------------------------------------------------------------------------------
1 | import { styled } from '@crepe-ui/styled-system/jsx'
2 |
3 | export const Text = styled.span
4 |
--------------------------------------------------------------------------------
/packages/components/src/ui/textarea.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentProps, forwardRef } from "react";
2 | import { HTMLStyledProps, styled } from "@crepe-ui/styled-system/jsx";
3 | import {
4 | TextareaVariantProps,
5 | textarea,
6 | } from "@crepe-ui/styled-system/recipes";
7 | import { useFormControl } from "./use-form-control";
8 | import { Assign } from "@crepe-ui/styled-system/types";
9 | import { FormControlOptions } from "./form-control-context";
10 | import { ark } from "@ark-ui/react";
11 |
12 | interface StyleProps extends HTMLStyledProps<"textarea"> {}
13 | interface JsxProps
14 | extends Assign, StyleProps> {}
15 |
16 | export interface TextareaProps
17 | extends Omit,
18 | TextareaVariantProps,
19 | FormControlOptions {}
20 |
21 | const StyledTextarea = styled(ark.textarea, textarea);
22 | export const Textarea = forwardRef(
23 | function Textarea(props, ref) {
24 | const fieldProps = useFormControl(props);
25 |
26 | return ;
27 | }
28 | );
29 | Textarea.displayName = "Textarea";
30 |
--------------------------------------------------------------------------------
/packages/components/src/ui/tooltip.tsx:
--------------------------------------------------------------------------------
1 | import * as Ark from "@ark-ui/react/tooltip";
2 | import { Assign } from "@crepe-ui/styled-system/types";
3 | import { HTMLStyledProps, styled } from "@crepe-ui/styled-system/jsx";
4 | import { TooltipVariantProps, tooltip } from "@crepe-ui/styled-system/recipes";
5 | import { createStyleContext } from "./create-style-context";
6 |
7 | const { withProvider, withContext } = createStyleContext(tooltip);
8 |
9 | // export * from '@ark-ui/react/tooltip'
10 | interface StyleProps extends HTMLStyledProps {}
11 | interface JsxProps extends Assign {}
12 |
13 | export interface TooltipProps extends JsxProps, TooltipVariantProps {}
14 |
15 | const TooltipRoot = withProvider(styled(Ark.Tooltip.Root));
16 | const TooltipArrow = withContext(styled(Ark.Tooltip.Arrow), "arrow");
17 | const TooltipArrowTip = withContext(styled(Ark.Tooltip.ArrowTip), "arrowTip");
18 | const TooltipContent = withContext(styled(Ark.Tooltip.Content), "content");
19 | const TooltipPositioner = withContext(
20 | styled(Ark.Tooltip.Positioner),
21 | "positioner"
22 | );
23 | const TooltipTrigger = withContext(styled(Ark.Tooltip.Trigger), "trigger");
24 |
25 | export const Tooltip = Object.assign(TooltipRoot, {
26 | Root: TooltipRoot,
27 | Arrow: TooltipArrow,
28 | ArrowTip: TooltipArrowTip,
29 | Content: TooltipContent,
30 | Positioner: TooltipPositioner,
31 | Trigger: TooltipTrigger,
32 | });
33 |
--------------------------------------------------------------------------------
/packages/components/src/ui/transition-utils.ts:
--------------------------------------------------------------------------------
1 | import type { Target, TargetAndTransition, Transition } from 'framer-motion'
2 |
3 | export type TransitionProperties = {
4 | /**
5 | * Custom `transition` definition for `enter` and `exit`
6 | */
7 | transition?: TransitionConfig
8 | /**
9 | * Custom `transitionEnd` definition for `enter` and `exit`
10 | */
11 | transitionEnd?: TransitionEndConfig
12 | /**
13 | * Custom `delay` definition for `enter` and `exit`
14 | */
15 | delay?: number | DelayConfig
16 | }
17 |
18 | type TargetResolver = (props: P & TransitionProperties) => TargetAndTransition
19 |
20 | type Variant
= TargetAndTransition | TargetResolver
21 |
22 | export type Variants
= {
23 | enter: Variant
24 | exit: Variant
25 | initial?: Variant
26 | }
27 |
28 | type WithMotionState
= Partial>
29 |
30 | export type TransitionConfig = WithMotionState
31 |
32 | export type TransitionEndConfig = WithMotionState
33 |
34 | export type DelayConfig = WithMotionState
35 |
36 | export const TRANSITION_EASINGS = {
37 | ease: [0.25, 0.1, 0.25, 1],
38 | easeIn: [0.4, 0, 1, 1],
39 | easeOut: [0, 0, 0.2, 1],
40 | easeInOut: [0.4, 0, 0.2, 1],
41 | } as const
42 |
43 | export const TRANSITION_VARIANTS = {
44 | scale: {
45 | enter: { scale: 1 },
46 | exit: { scale: 0.95 },
47 | },
48 | fade: {
49 | enter: { opacity: 1 },
50 | exit: { opacity: 0 },
51 | },
52 | pushLeft: {
53 | enter: { x: '100%' },
54 | exit: { x: '-30%' },
55 | },
56 | pushRight: {
57 | enter: { x: '-100%' },
58 | exit: { x: '30%' },
59 | },
60 | pushUp: {
61 | enter: { y: '100%' },
62 | exit: { y: '-30%' },
63 | },
64 | pushDown: {
65 | enter: { y: '-100%' },
66 | exit: { y: '30%' },
67 | },
68 | slideLeft: {
69 | position: { left: 0, top: 0, bottom: 0, width: '100%' },
70 | enter: { x: 0, y: 0 },
71 | exit: { x: '-100%', y: 0 },
72 | },
73 | slideRight: {
74 | position: { right: 0, top: 0, bottom: 0, width: '100%' },
75 | enter: { x: 0, y: 0 },
76 | exit: { x: '100%', y: 0 },
77 | },
78 | slideUp: {
79 | position: { top: 0, left: 0, right: 0, maxWidth: '100vw' },
80 | enter: { x: 0, y: 0 },
81 | exit: { x: 0, y: '-100%' },
82 | },
83 | slideDown: {
84 | position: { bottom: 0, left: 0, right: 0, maxWidth: '100vw' },
85 | enter: { x: 0, y: 0 },
86 | exit: { x: 0, y: '100%' },
87 | },
88 | }
89 |
90 | export type SlideDirection = 'top' | 'left' | 'bottom' | 'right'
91 |
92 | export function getSlideTransition(options?: { direction?: SlideDirection }) {
93 | const side = options?.direction ?? 'right'
94 | switch (side) {
95 | case 'right':
96 | return TRANSITION_VARIANTS.slideRight
97 | case 'left':
98 | return TRANSITION_VARIANTS.slideLeft
99 | case 'bottom':
100 | return TRANSITION_VARIANTS.slideDown
101 | case 'top':
102 | return TRANSITION_VARIANTS.slideUp
103 | default:
104 | return TRANSITION_VARIANTS.slideRight
105 | }
106 | }
107 |
108 | export const TRANSITION_DEFAULTS = {
109 | enter: {
110 | duration: 0.2,
111 | ease: TRANSITION_EASINGS.easeOut,
112 | },
113 | exit: {
114 | duration: 0.1,
115 | ease: TRANSITION_EASINGS.easeIn,
116 | },
117 | } as const
118 |
119 | export type WithTransitionConfig = Omit
&
120 | TransitionProperties & {
121 | /**
122 | * If `true`, the element will unmount when `in={false}` and animation is done
123 | */
124 | unmountOnExit?: boolean
125 | /**
126 | * Show the component; triggers when enter or exit states
127 | */
128 | in?: boolean
129 | }
130 |
131 | export const withDelay = {
132 | enter: (transition: Transition, delay?: number | DelayConfig): Transition & { delay: number | undefined } => ({
133 | ...transition,
134 | delay: typeof delay === 'number' ? delay : delay?.['enter'],
135 | }),
136 | exit: (transition: Transition, delay?: number | DelayConfig): Transition & { delay: number | undefined } => ({
137 | ...transition,
138 | delay: typeof delay === 'number' ? delay : delay?.['exit'],
139 | }),
140 | }
141 |
--------------------------------------------------------------------------------
/packages/components/src/ui/types.ts:
--------------------------------------------------------------------------------
1 | export type Optional = Pick, K> & Omit
2 |
--------------------------------------------------------------------------------
/packages/components/src/ui/use-form-control.ts:
--------------------------------------------------------------------------------
1 | import { ariaAttr, callAllHandlers } from '@crepe-ui/shared'
2 | import { FormControlOptions, useFormControlContext } from './form-control-context'
3 |
4 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/form-control/use-form-control.ts
5 |
6 | export interface UseFormControlProps extends FormControlOptions {
7 | id?: string
8 | onFocus?: React.FocusEventHandler
9 | onBlur?: React.FocusEventHandler
10 | disabled?: boolean
11 | readOnly?: boolean
12 | required?: boolean
13 | 'aria-describedby'?: string
14 | }
15 |
16 | /**
17 | * React hook that provides the props that should be spread on to
18 | * input fields (`input`, `select`, `textarea`, etc.).
19 | *
20 | * It provides a convenient way to control a form fields, validation
21 | * and helper text.
22 | *
23 | * @internal
24 | */
25 | export function useFormControl(props: UseFormControlProps) {
26 | const { isDisabled, isInvalid, isReadOnly, isRequired, ...rest } = useFormControlProps(props)
27 |
28 | return {
29 | ...rest,
30 | disabled: isDisabled,
31 | readOnly: isReadOnly,
32 | required: isRequired,
33 | 'aria-invalid': ariaAttr(isInvalid),
34 | 'aria-required': ariaAttr(isRequired),
35 | 'aria-readonly': ariaAttr(isReadOnly),
36 | }
37 | }
38 |
39 | /**
40 | * @internal
41 | */
42 | export function useFormControlProps(props: UseFormControlProps) {
43 | const field = useFormControlContext()
44 |
45 | const { id, disabled, readOnly, required, isRequired, isInvalid, isReadOnly, isDisabled, onFocus, onBlur, ...rest } =
46 | props
47 |
48 | const labelIds: string[] = props['aria-describedby'] ? [props['aria-describedby']] : []
49 |
50 | // Error message must be described first in all scenarios.
51 | if (field?.hasFeedbackText && field?.isInvalid) {
52 | labelIds.push(field.feedbackId)
53 | }
54 |
55 | if (field?.hasHelpText) {
56 | labelIds.push(field.helpTextId)
57 | }
58 |
59 | return {
60 | ...rest,
61 | 'aria-describedby': labelIds.join(' ') || undefined,
62 | id: id ?? field?.id,
63 | isDisabled: disabled ?? isDisabled ?? field?.isDisabled,
64 | isReadOnly: readOnly ?? isReadOnly ?? field?.isReadOnly,
65 | isRequired: required ?? isRequired ?? field?.isRequired,
66 | isInvalid: isInvalid ?? field?.isInvalid,
67 | onFocus: callAllHandlers(field?.onFocus, onFocus),
68 | onBlur: callAllHandlers(field?.onBlur, onBlur),
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/packages/components/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "compilerOptions": {
4 | "target": "ES2020",
5 | "useDefineForClassFields": true,
6 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
7 | "module": "ESNext",
8 | "skipLibCheck": true,
9 |
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "noEmit": true,
15 | "jsx": "react-jsx",
16 |
17 | "strict": true,
18 | "noUnusedLocals": true,
19 | "noUnusedParameters": true,
20 | "noFallthroughCasesInSwitch": true
21 | },
22 | "include": ["src", "demo"]
23 | }
24 |
--------------------------------------------------------------------------------
/packages/components/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.build.json",
3 | "compilerOptions": {
4 | "customConditions": ["source"]
5 | },
6 | "include": ["src", "demo"]
7 | }
8 |
--------------------------------------------------------------------------------
/packages/components/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsup'
2 |
3 | export default defineConfig({
4 | entry: ['src/components.ts'],
5 | clean: true,
6 | dts: true,
7 | format: ['esm', 'cjs'],
8 | external: ['@crepe-ui/styled-system', 'react', 'react-dom', '@ark-ui/react'],
9 | })
10 |
--------------------------------------------------------------------------------
/packages/components/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import dts from 'vite-plugin-dts'
3 | import * as path from 'path'
4 |
5 | export default defineConfig({
6 | plugins: [dts({ tsconfigPath: './tsconfig.build.json' })],
7 | resolve: {
8 | conditions: ['source'],
9 | alias: {
10 | '@crepe-ui/components': path.resolve(__dirname, '../components/src/components.ts'),
11 | },
12 | },
13 | build: {
14 | minify: true,
15 | lib: {
16 | entry: 'src/components.ts',
17 | fileName: 'index',
18 | formats: ['es', 'cjs'],
19 | },
20 | rollupOptions: {
21 | // external: ['react', 'react-dom'],
22 | external: ['@crepe-ui/styled-system', 'react', 'react-dom', '@ark-ui/react'],
23 | },
24 | },
25 | })
26 |
--------------------------------------------------------------------------------
/packages/preset-chakra/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@crepe-ui/preset-chakra",
3 | "version": "0.0.1",
4 | "types": "./src/preset.ts",
5 | "main": "./dist/preset.js",
6 | "exports": {
7 | ".": {
8 | "source": "./src/preset.ts",
9 | "types": "./dist/preset.d.ts",
10 | "import": "./dist/preset.mjs",
11 | "require": "./dist/preset.js"
12 | },
13 | "./vars": {
14 | "source": "./src/vars.ts",
15 | "types": "./dist/vars.d.ts",
16 | "import": {
17 | "types": "./dist/vars.d.mts",
18 | "default": "./dist/vars.mjs"
19 | },
20 | "require": "./dist/vars.js"
21 | },
22 | "./reset.css": "./reset.css"
23 | },
24 | "scripts": {
25 | "dev": "tsup --watch",
26 | "build": "tsup"
27 | },
28 | "dependencies": {
29 | "@ark-ui/anatomy": "^0.1.0",
30 | "@crepe-ui/shared": "workspace:^",
31 | "@pandacss/dev": "^0.27.1"
32 | },
33 | "devDependencies": {
34 | "tsup": "^7.2.0",
35 | "typescript": "^5.2.2"
36 | },
37 | "repository": {
38 | "type": "git",
39 | "url": "https://github.com/astahmer/crepe-ui",
40 | "directory": "packages/preset-chakra"
41 | },
42 | "sideEffects": false,
43 | "files": [
44 | "dist"
45 | ]
46 | }
47 |
--------------------------------------------------------------------------------
/packages/preset-chakra/reset.css:
--------------------------------------------------------------------------------
1 | /**
2 | https://github.com/chakra-ui/chakra-ui/blob/768aea0f0eb55af9d2e13d7568d92b0b995a0699/packages/components/src/css-reset/css-reset.tsx
3 | */
4 |
5 | @layer reset {
6 | html {
7 | line-height: 1.5;
8 | -webkit-text-size-adjust: 100%;
9 | font-family: system-ui, sans-serif;
10 | -webkit-font-smoothing: antialiased;
11 | text-rendering: optimizeLegibility;
12 | -moz-osx-font-smoothing: grayscale;
13 | touch-action: manipulation;
14 | }
15 |
16 | body {
17 | position: relative;
18 | min-height: 100%;
19 | margin: 0;
20 | font-feature-settings: 'kern';
21 | }
22 |
23 | :where(*, *::before, *::after) {
24 | border-width: 0;
25 | border-style: solid;
26 | box-sizing: border-box;
27 | word-wrap: break-word;
28 | }
29 |
30 | main {
31 | display: block;
32 | }
33 |
34 | hr {
35 | border-top-width: 1px;
36 | box-sizing: content-box;
37 | height: 0;
38 | overflow: visible;
39 | }
40 |
41 | :where(pre, code, kbd, samp) {
42 | font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace;
43 | font-size: 1em;
44 | }
45 |
46 | a {
47 | background-color: transparent;
48 | color: inherit;
49 | text-decoration: inherit;
50 | }
51 |
52 | abbr[title] {
53 | border-bottom: none;
54 | text-decoration: underline;
55 | -webkit-text-decoration: underline dotted;
56 | text-decoration: underline dotted;
57 | }
58 |
59 | :where(b, strong) {
60 | font-weight: bold;
61 | }
62 |
63 | small {
64 | font-size: 80%;
65 | }
66 |
67 | :where(sub, sup) {
68 | font-size: 75%;
69 | line-height: 0;
70 | position: relative;
71 | vertical-align: baseline;
72 | }
73 |
74 | sub {
75 | bottom: -0.25em;
76 | }
77 |
78 | sup {
79 | top: -0.5em;
80 | }
81 |
82 | img {
83 | border-style: none;
84 | }
85 |
86 | :where(button, input, optgroup, select, textarea) {
87 | font-family: inherit;
88 | font-size: 100%;
89 | line-height: 1.15;
90 | margin: 0;
91 | }
92 |
93 | :where(button, input) {
94 | overflow: visible;
95 | }
96 |
97 | :where(button, select) {
98 | text-transform: none;
99 | }
100 |
101 | :where(
102 | button::-moz-focus-inner,
103 | [type='button']::-moz-focus-inner,
104 | [type='reset']::-moz-focus-inner,
105 | [type='submit']::-moz-focus-inner
106 | ) {
107 | border-style: none;
108 | padding: 0;
109 | }
110 |
111 | fieldset {
112 | padding: 0.35em 0.75em 0.625em;
113 | }
114 |
115 | legend {
116 | box-sizing: border-box;
117 | color: inherit;
118 | display: table;
119 | max-width: 100%;
120 | padding: 0;
121 | white-space: normal;
122 | }
123 |
124 | progress {
125 | vertical-align: baseline;
126 | }
127 |
128 | textarea {
129 | overflow: auto;
130 | }
131 |
132 | :where([type='checkbox'], [type='radio']) {
133 | box-sizing: border-box;
134 | padding: 0;
135 | }
136 |
137 | input[type='number']::-webkit-inner-spin-button,
138 | input[type='number']::-webkit-outer-spin-button {
139 | -webkit-appearance: none !important;
140 | }
141 |
142 | input[type='number'] {
143 | -moz-appearance: textfield;
144 | }
145 |
146 | input[type='search'] {
147 | -webkit-appearance: textfield;
148 | outline-offset: -2px;
149 | }
150 |
151 | input[type='search']::-webkit-search-decoration {
152 | -webkit-appearance: none !important;
153 | }
154 |
155 | ::-webkit-file-upload-button {
156 | -webkit-appearance: button;
157 | font: inherit;
158 | }
159 |
160 | details {
161 | display: block;
162 | }
163 |
164 | summary {
165 | display: list-item;
166 | }
167 |
168 | template {
169 | display: none;
170 | }
171 |
172 | [hidden] {
173 | display: none !important;
174 | }
175 |
176 | :where(blockquote, dl, dd, h1, h2, h3, h4, h5, h6, hr, figure, p, pre) {
177 | margin: 0;
178 | }
179 |
180 | button {
181 | background: transparent;
182 | padding: 0;
183 | }
184 |
185 | fieldset {
186 | margin: 0;
187 | padding: 0;
188 | }
189 |
190 | :where(ol, ul) {
191 | margin: 0;
192 | padding: 0;
193 | }
194 |
195 | textarea {
196 | resize: vertical;
197 | }
198 |
199 | :where(button, [role='button']) {
200 | cursor: pointer;
201 | }
202 |
203 | button::-moz-focus-inner {
204 | border: 0 !important;
205 | }
206 |
207 | table {
208 | border-collapse: collapse;
209 | }
210 |
211 | :where(h1, h2, h3, h4, h5, h6) {
212 | font-size: inherit;
213 | font-weight: inherit;
214 | }
215 |
216 | :where(button, input, optgroup, select, textarea) {
217 | padding: 0;
218 | line-height: inherit;
219 | color: inherit;
220 | }
221 |
222 | :where(img, svg, video, canvas, audio, iframe, embed, object) {
223 | display: block;
224 | }
225 |
226 | :where(img, video) {
227 | max-width: 100%;
228 | height: auto;
229 | }
230 |
231 | [data-js-focus-visible]
232 | :focus:not([data-focus-visible-added]):not([data-focus-visible-disabled]) {
233 | outline: none;
234 | box-shadow: none;
235 | }
236 |
237 | select::-ms-expand {
238 | display: none;
239 | }
240 |
241 | :root,
242 | :host {
243 | --chakra-vh: 100vh;
244 | }
245 |
246 | @supports (height: -webkit-fill-available) {
247 | :root,
248 | :host {
249 | --chakra-vh: -webkit-fill-available;
250 | }
251 | }
252 |
253 | @supports (height: -moz-fill-available) {
254 | :root,
255 | :host {
256 | --chakra-vh: -moz-fill-available;
257 | }
258 | }
259 |
260 | @supports (height: 100dvh) {
261 | :root,
262 | :host {
263 | --chakra-vh: 100dvh;
264 | }
265 | }
266 | }
267 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/conditions.ts:
--------------------------------------------------------------------------------
1 | export const conditions = {
2 | extend: {
3 | dark: '.dark &, [data-theme="dark"] &',
4 | light: '.light &, [data-theme="light"] &',
5 | closed: '&:is([data-state=closed])',
6 | hidden: '&[hidden], &[data-hidden]',
7 | },
8 | }
9 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/default-config.ts:
--------------------------------------------------------------------------------
1 | import { Config } from '@pandacss/dev'
2 |
3 | const staticCss: Config['staticCss'] = {
4 | css: [{ properties: { colorPalette: ['*'] } }],
5 | recipes: {
6 | alert: ['*'],
7 | avatar: ['*'],
8 | button: ['*'],
9 | card: ['*'],
10 | checkbox: ['*'],
11 | closeButton: ['*'],
12 | code: ['*'],
13 | badge: ['*'],
14 | formControl: ['*'],
15 | heading: ['*'],
16 | icon: ['*'],
17 | input: ['*'],
18 | kbd: ['*'],
19 | link: ['*'],
20 | modal: ['*'],
21 | popover: ['*'],
22 | select: ['*'],
23 | skeleton: ['*'],
24 | switchRecipe: ['*'],
25 | tabs: ['*'],
26 | table: ['*'],
27 | tag: ['*'],
28 | textarea: ['*'],
29 | tooltip: ['*'],
30 | },
31 | }
32 |
33 | export const defaultConfig: Config = {
34 | staticCss,
35 | presets: ['@pandacss/preset-panda', '@crepe-ui/preset-chakra'],
36 | }
37 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/global-styles.ts:
--------------------------------------------------------------------------------
1 | import { defineGlobalStyles } from '@pandacss/dev'
2 | import { defaultConfig } from './default-config'
3 |
4 | export const globalStyles = defineGlobalStyles({
5 | [`${defaultConfig.cssVarRoot} body`]: {
6 | fontFamily: 'body',
7 | color: 'chakra-body-text',
8 | bg: 'chakra-body-bg',
9 | transitionProperty: 'background-color',
10 | transitionDuration: 'normal',
11 | lineHeight: 'base',
12 | },
13 | [`${defaultConfig.cssVarRoot} *::placeholder`]: {
14 | color: 'chakra-placeholder-color',
15 | },
16 | [`${defaultConfig.cssVarRoot} *, *::before, &::after`]: {
17 | borderColor: 'chakra-border-color',
18 | },
19 | })
20 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/keyframes.ts:
--------------------------------------------------------------------------------
1 | import { defineKeyframes } from '@pandacss/dev'
2 | import { cssVariables } from './vars'
3 |
4 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/checkbox/checkbox.tsx
5 | const checkboxKeyframes = defineKeyframes({
6 | checkAnim: {
7 | from: { opacity: 0, strokeDashoffset: 16, transform: 'scale(0.95)' },
8 | to: { opacity: 1, strokeDashoffset: 0, transform: 'scale(1)' },
9 | },
10 | indeterminateOpacityAnim: {
11 | from: { opacity: 0 },
12 | to: { opacity: 1 },
13 | },
14 | indeterminateScaleAnim: {
15 | from: { transform: 'scaleX(0.65)' },
16 | to: { transform: 'scaleX(1)' },
17 | },
18 | })
19 |
20 | // https://github.com/cschroeter/park-ui/blob/ff729b25267137a6e5a87a375fe8b647e148dd2b/packages/presets/src/theme/keyframes.ts#L12
21 | export const keyframes = defineKeyframes({
22 | ...checkboxKeyframes,
23 | fadeIn: {
24 | from: { opacity: '0' },
25 | to: { opacity: '1' },
26 | },
27 | fadeOut: {
28 | from: { opacity: '1' },
29 | to: { opacity: '0' },
30 | },
31 | 'fade-in': {
32 | from: { opacity: '0' },
33 | to: { opacity: '1' },
34 | },
35 | 'fade-out': {
36 | from: { opacity: '1' },
37 | to: { opacity: '0' },
38 | },
39 | 'slide-in': {
40 | '0%': { opacity: '0', transform: 'translateY(64px)' },
41 | '100%': { opacity: '1', transform: 'translateY(0)' },
42 | },
43 | 'slide-out': {
44 | '0%': { opacity: '1', transform: 'translateY(0)' },
45 | '100%': { opacity: '0', transform: 'translateY(64px)' },
46 | },
47 | 'skeleton-bg-fade': {
48 | from: {
49 | borderColor: cssVariables.skeleton['start-color'].ref,
50 | background: cssVariables.skeleton['start-color'].ref,
51 | },
52 | to: {
53 | borderColor: cssVariables.skeleton['end-color'].ref,
54 | background: cssVariables.skeleton['end-color'].ref,
55 | },
56 | },
57 | spin: {
58 | '0%': {
59 | transform: 'rotate(0deg)',
60 | },
61 | '100%': {
62 | transform: 'rotate(360deg)',
63 | },
64 | },
65 | })
66 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/preset.ts:
--------------------------------------------------------------------------------
1 | import { definePreset } from '@pandacss/dev'
2 | import { conditions } from './conditions'
3 | import { globalStyles } from './global-styles'
4 | import { keyframes } from './keyframes'
5 | import { generatePreflight } from './preflight'
6 | import { chakraRecipes } from './recipes'
7 | import { semanticTokens } from './semantic-tokens'
8 | import { textStyles } from './text-styles'
9 | import { tokens } from './tokens'
10 | import { utilities } from './utilities'
11 |
12 | export default definePreset({
13 | globalCss: globalStyles,
14 | utilities,
15 | conditions,
16 | theme: {
17 | extend: {
18 | semanticTokens,
19 | tokens,
20 | recipes: chakraRecipes,
21 | keyframes,
22 | textStyles,
23 | },
24 | },
25 | })
26 |
27 | export { defaultConfig } from './default-config'
28 | export { chakraRecipes, generatePreflight }
29 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes.ts:
--------------------------------------------------------------------------------
1 | import { alertRecipe } from './recipes/alert.recipe'
2 | import { avatarRecipe } from './recipes/avatar.recipe'
3 | import { badgeRecipe } from './recipes/badge.recipe'
4 | import { buttonRecipe } from './recipes/button.recipe'
5 | import { cardRecipe } from './recipes/card.recipe'
6 | import { checkboxRecipe } from './recipes/checkbox.recipe'
7 | import { closeButtonRecipe } from './recipes/close-button.recipe'
8 | import { codeRecipe } from './recipes/code.recipe'
9 | import { formControlRecipe } from './recipes/form-control.recipe'
10 | import { headingRecipe } from './recipes/heading.recipe'
11 | import { iconRecipe } from './recipes/icon.recipe'
12 | import { inputRecipe } from './recipes/input.recipe'
13 | import { kbdRecipe } from './recipes/kbd.recipe'
14 | import { linkRecipe } from './recipes/link.recipe'
15 | import { modalRecipe } from './recipes/modal.recipe'
16 | import { popoverRecipe } from './recipes/popover.recipe'
17 | import { selectRecipe } from './recipes/select.recipe'
18 | import { skeletonRecipe } from './recipes/skeleton.recipe'
19 | import { switchRecipe } from './recipes/switch.recipe'
20 | import { tableRecipe } from './recipes/table.recipe'
21 | import { tabsRecipe } from './recipes/tabs.recipe'
22 | import { tagRecipe } from './recipes/tag.recipe'
23 | import { textareaRecipe } from './recipes/textarea.recipe'
24 | import { tooltipRecipe } from './recipes/tooltip.recipe'
25 |
26 | export const chakraRecipes = {
27 | alert: alertRecipe,
28 | avatar: avatarRecipe,
29 | button: buttonRecipe,
30 | card: cardRecipe,
31 | checkbox: checkboxRecipe,
32 | closeButton: closeButtonRecipe,
33 | code: codeRecipe,
34 | badge: badgeRecipe,
35 | formControl: formControlRecipe,
36 | heading: headingRecipe,
37 | icon: iconRecipe,
38 | input: inputRecipe,
39 | kbd: kbdRecipe,
40 | link: linkRecipe,
41 | modal: modalRecipe,
42 | popover: popoverRecipe,
43 | select: selectRecipe,
44 | skeleton: skeletonRecipe,
45 | switchRecipe: switchRecipe,
46 | tabs: tabsRecipe,
47 | table: tableRecipe,
48 | tag: tagRecipe,
49 | textarea: textareaRecipe,
50 | tooltip: tooltipRecipe,
51 | }
52 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/alert.recipe.ts:
--------------------------------------------------------------------------------
1 | import { colorMixVar, cssVar } from '@crepe-ui/shared'
2 | import { defineSlotRecipe } from '@pandacss/dev'
3 |
4 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/alert/alert.tsx
5 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/alert/alert-icon.tsx
6 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/alert.ts
7 |
8 | const vars = cssVar.scope('alert', ['fg', 'bg', 'bg-alpha'])
9 | const bgDark = colorMixVar('colorPalette.200/16', vars['bg-alpha'])
10 |
11 | export const alertRecipe = defineSlotRecipe({
12 | className: 'alert',
13 | slots: ['container', 'icon', 'spinner', 'title', 'description'],
14 | base: {
15 | container: {
16 | colorPalette: 'blue',
17 | bg: vars.bg.ref,
18 | px: '4',
19 | py: '3',
20 | //
21 | width: '100%',
22 | display: 'flex',
23 | alignItems: 'center',
24 | position: 'relative',
25 | overflow: 'hidden',
26 | },
27 | title: {
28 | fontWeight: 'bold',
29 | lineHeight: '6',
30 | marginEnd: '2',
31 | },
32 | description: {
33 | lineHeight: '6',
34 | },
35 | icon: {
36 | color: vars.fg.ref,
37 | flexShrink: 0,
38 | marginEnd: '3',
39 | w: '5',
40 | h: '6',
41 | //
42 | display: 'inherit',
43 | },
44 | spinner: {
45 | color: vars.fg.ref,
46 | flexShrink: 0,
47 | marginEnd: '3',
48 | w: '5',
49 | h: '5',
50 | },
51 | },
52 | variants: {
53 | variant: {
54 | subtle: {
55 | container: {
56 | [vars.fg.name]: 'colors.colorPalette.500',
57 | [vars.bg.name]: 'colors.colorPalette.100',
58 | _dark: {
59 | [vars.fg.name]: 'colors.colorPalette.200',
60 | [vars['bg-alpha'].name]: bgDark.colorMixValue,
61 | [vars.bg.name]: bgDark.value,
62 | },
63 | },
64 | },
65 | 'left-accent': {
66 | container: {
67 | [vars.fg.name]: 'colors.colorPalette.500',
68 | [vars.bg.name]: 'colors.colorPalette.100',
69 | _dark: {
70 | [vars.fg.name]: 'colors.colorPalette.200',
71 | [vars['bg-alpha'].name]: bgDark.colorMixValue,
72 | },
73 | paddingStart: '3',
74 | borderStartWidth: '4px',
75 | borderStartColor: vars.fg.ref,
76 | },
77 | },
78 | 'top-accent': {
79 | container: {
80 | [vars.fg.name]: 'colors.colorPalette.500',
81 | [vars.bg.name]: 'colors.colorPalette.100',
82 | _dark: {
83 | [vars.fg.name]: 'colors.colorPalette.200',
84 | [vars.bg.name]: bgDark.value,
85 | },
86 | pt: '2',
87 | borderTopWidth: '4px',
88 | borderTopColor: vars.fg.ref,
89 | },
90 | },
91 | solid: {
92 | container: {
93 | [vars.fg.name]: 'colors.white',
94 | [vars.bg.name]: 'colors.colorPalette.500',
95 | _dark: {
96 | [vars.fg.name]: 'colors.gray.900',
97 | [vars.bg.name]: 'colors.colorPalette.200',
98 | },
99 | color: vars.fg.ref,
100 | },
101 | },
102 | },
103 | },
104 | defaultVariants: {
105 | variant: 'subtle',
106 | },
107 | })
108 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/avatar.recipe.ts:
--------------------------------------------------------------------------------
1 | import { defineSlotRecipe } from '@pandacss/dev'
2 | import { cssVar } from '@crepe-ui/shared'
3 |
4 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/avatar/avatar.tsx
5 | // https://github.com/chakra-ui/chakra-ui/blob/b904bbccab7d9c3b7ead48043b0e0652701f31f8/packages/theme/src/components/avatar.ts
6 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/avatar/avatar-image.tsx
7 |
8 | const vars = cssVar.scope('avatar', ['border-color', ['border-radius', 'token(sizes.full)'], 'bg', 'font-size', 'size'])
9 |
10 | function getSize(size: string | number, fontSize: string | number) {
11 | return {
12 | root: {
13 | [vars.size.name]: size,
14 | [vars['font-size'].name]: fontSize,
15 | },
16 | }
17 | }
18 |
19 | export const avatarRecipe = defineSlotRecipe({
20 | className: 'avatar',
21 | slots: ['root', 'fallback', 'image'],
22 | base: {
23 | root: {
24 | bg: vars.bg.ref,
25 | fontSize: vars['font-size'].ref,
26 | color: { base: 'white', '&[data-is-bg-dark]': 'gray.800' },
27 | borderColor: vars['border-color'].ref,
28 | verticalAlign: 'top',
29 | width: vars.size.ref,
30 | height: vars.size.ref,
31 | '&:not([data-loaded])': {
32 | [vars.bg.name]: 'colors.gray.400',
33 | },
34 | [vars['border-color'].name]: 'colors.white',
35 | _dark: {
36 | [vars['border-color'].name]: 'colors.gray.800',
37 | },
38 | //
39 | display: 'inline-flex',
40 | // alignItems: 'center',
41 | justifyContent: 'center',
42 | textAlign: 'center',
43 | textTransform: 'uppercase',
44 | fontWeight: 'medium',
45 | position: 'relative',
46 | flexShrink: 0,
47 | borderRadius: vars['border-radius'].ref,
48 | },
49 | fallback: {
50 | alignItems: 'center',
51 | // background: 'gray.300',
52 | // borderWidth: '1px',
53 | display: 'flex',
54 | fontWeight: 'semibold',
55 | height: 'inherit',
56 | justifyContent: 'center',
57 | },
58 | image: {
59 | width: '100%',
60 | height: '100%',
61 | objectFit: 'cover',
62 | borderRadius: vars['border-radius'].ref,
63 | },
64 | },
65 | variants: {
66 | size: {
67 | '2xs': getSize('sizes.4', 'fontSizes.3xs'),
68 | xs: getSize('sizes.6', 'fontSizes.2xs'),
69 | sm: getSize('sizes.8', 'fontSizes.xs'),
70 | md: getSize('sizes.12', 'fontSizes.md'),
71 | lg: getSize('sizes.16', 'fontSizes.lg'),
72 | xl: getSize('sizes.24', 'fontSizes.3xl'),
73 | '2xl': getSize('sizes.32', 'fontSizes.4xl'),
74 | },
75 | },
76 | defaultVariants: {
77 | size: 'md',
78 | },
79 | })
80 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/badge.recipe.ts:
--------------------------------------------------------------------------------
1 | import { defineRecipe } from '@pandacss/dev'
2 | import { colorMixVar } from '@crepe-ui/shared'
3 | import { cssVariables } from '../vars'
4 |
5 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/layout/badge.tsx
6 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/badge.ts
7 |
8 | const vars = cssVariables.badge
9 | const bgDark = {
10 | solid: colorMixVar('colorPalette.500/40', vars['bg-alpha']),
11 | subtle: colorMixVar('colorPalette.200/16', vars['bg-alpha']),
12 | outline: colorMixVar('colorPalette.200/20', vars['bg-alpha']),
13 | }
14 |
15 | export { vars as badgeVars }
16 |
17 | export const badgeRecipe = defineRecipe({
18 | className: 'badge',
19 | base: {
20 | colorPalette: 'gray',
21 | px: 1,
22 | textTransform: 'uppercase',
23 | fontSize: 'xs',
24 | borderRadius: 'sm',
25 | fontWeight: 'bold',
26 | bg: vars.bg.ref,
27 | color: vars.color.ref,
28 | boxShadow: vars.shadow.ref,
29 | //
30 | display: 'inline-block',
31 | whiteSpace: 'nowrap',
32 | verticalAlign: 'middle',
33 | },
34 | variants: {
35 | variant: {
36 | solid: {
37 | [vars.bg.name]: 'colors.colorPalette.500',
38 | [vars.color.name]: 'colors.white',
39 | _dark: {
40 | [vars['bg-alpha'].name]: bgDark.solid.colorMixValue,
41 | [vars.bg.name]: bgDark.solid.value,
42 | [vars.color.name]: 'colors.whiteAlpha.800',
43 | },
44 | },
45 | subtle: {
46 | [vars.bg.name]: 'colors.colorPalette.100',
47 | [vars.color.name]: 'colors.colorPalette.800',
48 | _dark: {
49 | [vars['bg-alpha'].name]: bgDark.subtle.colorMixValue,
50 | [vars.bg.name]: bgDark.subtle.value,
51 | [vars.color.name]: 'colors.colorPalette.200',
52 | },
53 | },
54 | outline: {
55 | [vars.color.name]: 'colors.colorPalette.500',
56 | _dark: {
57 | [vars['bg-alpha'].name]: bgDark.outline.colorMixValue,
58 | [vars.bg.name]: bgDark.outline.value,
59 | },
60 | [vars.shadow.name]: `inset 0 0 0px 1px ${vars.color.ref}`,
61 | },
62 | },
63 | },
64 | defaultVariants: {
65 | variant: 'subtle',
66 | },
67 | })
68 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/button.recipe.ts:
--------------------------------------------------------------------------------
1 | import { defineRecipe, defineStyles } from '@pandacss/dev'
2 |
3 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/button.ts
4 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/button/button.tsx#L60
5 |
6 | const variantGhostStyles = defineStyles({
7 | color: { base: `colorPalette.600`, _dark: `colorPalette.200` },
8 | bg: 'transparent',
9 | _hover: {
10 | backgroundAlpha: { base: `colorPalette.50`, _dark: 'colorPalette.200/12' },
11 | },
12 | _active: {
13 | backgroundAlpha: { base: `colorPalette.100`, _dark: 'colorPalette.200/24' },
14 | },
15 | })
16 |
17 | export const buttonRecipe = defineRecipe({
18 | className: 'btn',
19 | base: {
20 | lineHeight: '1.2',
21 | borderRadius: 'md',
22 | fontWeight: 'semibold',
23 | transitionProperty: 'common',
24 | transitionDuration: 'normal',
25 | _focusVisible: { boxShadow: 'outline' },
26 | _disabled: { opacity: 0.4, cursor: 'not-allowed', boxShadow: 'none' },
27 | _hover: { _disabled: { bg: 'initial' } },
28 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/button/button.tsx#L60
29 | display: 'inline-flex',
30 | appearance: 'none',
31 | alignItems: 'center',
32 | justifyContent: 'center',
33 | userSelect: 'none',
34 | position: 'relative',
35 | whiteSpace: 'nowrap',
36 | verticalAlign: 'middle',
37 | outline: 'none',
38 | _focus: {
39 | zIndex: 1,
40 | },
41 | },
42 |
43 | variants: {
44 | variant: {
45 | ghost: variantGhostStyles,
46 | outline: {
47 | border: '1px solid',
48 | borderColor: {
49 | base: 'currentColor',
50 | },
51 | ...variantGhostStyles, // Since outline uses ghost styles as well
52 | },
53 | solid: {
54 | bg: { base: 'colorPalette.500', _dark: 'colorPalette.200' },
55 | color: { base: 'white', _dark: 'gray.800' },
56 | _hover: {
57 | bg: { base: 'colorPalette.600', _dark: 'colorPalette.300' },
58 | _disabled: {
59 | bg: { base: 'colorPalette.500', _dark: 'colorPalette.200' },
60 | },
61 | },
62 | _active: {
63 | bg: { base: 'colorPalette.700', _dark: 'colorPalette.400' },
64 | },
65 | },
66 | link: {
67 | padding: 0,
68 | height: 'auto',
69 | lineHeight: 'normal',
70 | verticalAlign: 'baseline',
71 | color: { base: 'colorPalette.500', _dark: 'colorPalette.200' },
72 | _hover: {
73 | textDecoration: 'underline',
74 | _disabled: { textDecoration: 'none' },
75 | },
76 | _active: {
77 | color: { base: 'colorPalette.700', _dark: 'colorPalette.500' },
78 | },
79 | },
80 | unstyled: {
81 | bg: 'none',
82 | color: 'inherit',
83 | display: 'inline',
84 | lineHeight: 'inherit',
85 | m: '0',
86 | p: '0',
87 | },
88 | },
89 | colorPalette: {
90 | gray: {},
91 | },
92 | size: {
93 | lg: { h: '12', minW: '12', fontSize: 'lg', px: '6' },
94 | md: { h: '10', minW: '10', fontSize: 'md', px: '4' },
95 | sm: { h: '8', minW: '8', fontSize: 'sm', px: '3' },
96 | xs: { h: '6', minW: '6', fontSize: 'xs', px: '2' },
97 | },
98 | },
99 | compoundVariants: [
100 | {
101 | variant: 'ghost',
102 | colorPalette: 'gray',
103 | css: {
104 | color: { base: 'gray.800', _dark: 'whiteAlpha.900' },
105 | _hover: { bg: { base: 'gray.100', _dark: 'whiteAlpha.200' } },
106 | _active: { bg: { base: 'gray.200', _dark: 'whiteAlpha.300' } },
107 | },
108 | },
109 | {
110 | variant: 'outline',
111 | colorPalette: 'gray',
112 | css: {
113 | borderColor: { base: 'gray.200', _dark: 'whiteAlpha.300' },
114 | },
115 | },
116 | {
117 | variant: 'solid',
118 | colorPalette: 'gray',
119 | css: {
120 | bg: { base: 'gray.100', _dark: 'whiteAlpha.200' },
121 | color: { base: 'gray.800', _dark: 'whiteAlpha.900' },
122 | _hover: {
123 | bg: { base: 'gray.200', _dark: 'whiteAlpha.300' },
124 | _disabled: {
125 | bg: { base: 'gray.100', _dark: 'whiteAlpha.200' },
126 | },
127 | },
128 | _active: { bg: { base: 'gray.300', _dark: 'whiteAlpha.400' } },
129 | },
130 | },
131 | ],
132 | defaultVariants: {
133 | colorPalette: 'gray',
134 | variant: 'solid',
135 | size: 'md',
136 | },
137 | })
138 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/card.recipe.ts:
--------------------------------------------------------------------------------
1 | import { cssVar } from '@crepe-ui/shared'
2 | import { defineSlotRecipe } from '@pandacss/dev'
3 |
4 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/card.ts
5 | // packages/components/src/card/card.tsx
6 |
7 | const vars = cssVar.scope('card', ['bg', 'padding', 'shadow', 'radius', ['border-width', '0'], 'border-color'])
8 |
9 | export const cardRecipe = defineSlotRecipe({
10 | className: 'card',
11 | slots: ['container', 'body', 'header', 'footer'],
12 | base: {
13 | container: {
14 | [vars.bg.name]: 'colors.chakra-body-bg',
15 | backgroundColor: vars.bg.ref,
16 | boxShadow: vars.shadow.ref,
17 | borderRadius: vars.radius.ref,
18 | color: 'chakra-body-text',
19 | borderWidth: vars['border-width'].ref,
20 | borderColor: vars['border-color'].ref,
21 | },
22 | body: {
23 | padding: vars.padding.ref,
24 | flex: '1 1 0%',
25 | },
26 | header: {
27 | padding: vars.padding.ref,
28 | },
29 | footer: {
30 | padding: vars.padding.ref,
31 | },
32 | },
33 | variants: {
34 | variant: {
35 | elevated: {
36 | container: {
37 | [vars.shadow.name]: 'shadows.base',
38 | _dark: {
39 | [vars.bg.name]: 'colors.gray.700',
40 | },
41 | },
42 | },
43 | outline: {
44 | container: {
45 | [vars['border-width'].name]: '1px',
46 | [vars['border-color'].name]: 'colors.chakra-border-color',
47 | _dark: {
48 | [vars['border-color'].name]: 'colors.whiteAlpha.300',
49 | },
50 | },
51 | },
52 | filled: {
53 | container: {
54 | [vars.bg.name]: 'colors.chakra-subtle-bg',
55 | },
56 | },
57 | unstyled: {
58 | body: {
59 | [vars.padding.name]: 0,
60 | },
61 | header: {
62 | [vars.padding.name]: 0,
63 | },
64 | footer: {
65 | [vars.padding.name]: 0,
66 | },
67 | },
68 | },
69 | size: {
70 | sm: {
71 | container: {
72 | [vars.radius.name]: 'radii.base',
73 | [vars.padding.name]: 'space.3',
74 | },
75 | },
76 | md: {
77 | container: {
78 | [vars.radius.name]: 'radii.md',
79 | [vars.padding.name]: 'space.5',
80 | },
81 | },
82 | lg: {
83 | container: {
84 | [vars.radius.name]: 'radii.xl',
85 | [vars.padding.name]: 'space.7',
86 | },
87 | },
88 | },
89 | },
90 | defaultVariants: {
91 | variant: 'elevated',
92 | size: 'md',
93 | },
94 | })
95 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/checkbox.recipe.ts:
--------------------------------------------------------------------------------
1 | import { cssVar } from '@crepe-ui/shared'
2 | import { defineSlotRecipe } from '@pandacss/dev'
3 |
4 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/checkbox.ts
5 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/checkbox/checkbox.tsx
6 |
7 | const $size = cssVar.create('checkbox-size', '1em')
8 |
9 | /**
10 | * chakra v3 parts - ark-ui slots
11 | *
12 | * @prop icon = -
13 | * @prop container = root
14 | * @prop control = control
15 | * @prop label = label
16 | */
17 | export const checkboxRecipe = defineSlotRecipe({
18 | className: 'checkbox',
19 | slots: ['container', 'control', 'label', 'icon'],
20 | base: {
21 | container: {
22 | cursor: 'pointer',
23 | display: 'inline-flex',
24 | alignItems: 'center',
25 | verticalAlign: 'top',
26 | position: 'relative',
27 | //
28 | _disabled: { cursor: 'not-allowed' },
29 | //
30 | colorPalette: 'blue',
31 | },
32 | control: {
33 | display: 'inline-flex',
34 | alignItems: 'center',
35 | justifyContent: 'center',
36 | verticalAlign: 'top',
37 | userSelect: 'none',
38 | flexShrink: 0,
39 | //
40 | w: $size.ref,
41 | h: $size.ref,
42 | transitionProperty: 'box-shadow',
43 | transitionDuration: 'normal',
44 | border: '2px solid',
45 | borderRadius: 'sm',
46 | // borderColor: 'inherit',
47 | color: 'white',
48 | borderColor: { base: 'gray.200', _dark: 'whiteAlpha.300' },
49 |
50 | _checked: {
51 | bg: { base: 'colorPalette.500', _dark: 'colorPalette.200' },
52 | borderColor: { base: 'colorPalette.500', _dark: 'colorPalette.200' },
53 | color: { base: 'white', _dark: 'gray.900' },
54 |
55 | _hover: {
56 | bg: { base: 'colorPalette.600', _dark: 'colorPalette.300' },
57 | borderColor: {
58 | base: 'colorPalette.600',
59 | _dark: 'colorPalette.300',
60 | },
61 | },
62 |
63 | _disabled: {
64 | borderColor: { base: 'gray.200', _dark: 'transparent' },
65 | bg: { base: 'gray.200', _dark: 'whiteAlpha.300' },
66 | color: { base: 'gray.500', _dark: 'whiteAlpha.500' },
67 | },
68 | },
69 |
70 | _indeterminate: {
71 | bg: { base: 'colorPalette.500', _dark: 'colorPalette.200' },
72 | borderColor: { base: 'colorPalette.500', _dark: 'colorPalette.200' },
73 | color: { base: 'white', _dark: 'gray.900' },
74 | },
75 |
76 | _disabled: {
77 | bg: { base: 'gray.100', _dark: 'whiteAlpha.100' },
78 | borderColor: { base: 'gray.100', _dark: 'transparent' },
79 | },
80 |
81 | _focusVisible: {
82 | boxShadow: 'outline',
83 | },
84 |
85 | _invalid: {
86 | borderColor: { base: 'red.500', _dark: 'red.300' },
87 | },
88 | },
89 | label: {
90 | ml: '0.5rem',
91 | userSelect: 'none',
92 | _disabled: { opacity: 0.4 },
93 | },
94 | icon: {
95 | animation: 'checkAnim 200ms linear',
96 | fontSize: $size.ref,
97 | color: 'var(--checkbox-icon-color, unset)',
98 | _indeterminate: {
99 | animation: 'indeterminateOpacityAnim 20ms linear, indeterminateScaleAnim 200ms linear',
100 | },
101 | //
102 | transitionProperty: 'transform',
103 | transitionDuration: 'normal',
104 | },
105 | },
106 | variants: {
107 | size: {
108 | sm: {
109 | control: { [$size.name]: 'sizes.3' },
110 | label: { fontSize: 'sm' },
111 | icon: { fontSize: '3xs' },
112 | },
113 | md: {
114 | control: { [$size.name]: 'sizes.4' },
115 | label: { fontSize: 'md' },
116 | icon: { fontSize: '2xs' },
117 | },
118 | lg: {
119 | control: { [$size.name]: 'sizes.5' },
120 | label: { fontSize: 'lg' },
121 | icon: { fontSize: '2xs' },
122 | },
123 | },
124 | shape: {
125 | circular: { control: { rounded: 'full' } },
126 | },
127 | },
128 | defaultVariants: {
129 | size: 'md',
130 | },
131 | })
132 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/close-button.recipe.ts:
--------------------------------------------------------------------------------
1 | import { defineRecipe } from '@pandacss/dev'
2 | import { cssVar } from '@crepe-ui/shared'
3 |
4 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/modal.ts
5 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/close-button/close-button.tsx
6 |
7 | const $size = cssVar.create('btn-bg')
8 | const $bg = cssVar.create('btn-size')
9 |
10 | export const closeButtonRecipe = defineRecipe({
11 | className: 'close-btn',
12 | base: {
13 | w: $size.ref,
14 | h: $size.ref,
15 | borderRadius: 'md',
16 | transitionProperty: 'common',
17 | transitionDuration: 'normal',
18 | _disabled: {
19 | opacity: 0.4,
20 | cursor: 'not-allowed',
21 | boxShadow: 'none',
22 | },
23 | _hover: {
24 | [$bg.name]: 'colors.blackAlpha.100',
25 | _dark: {
26 | [$bg.name]: 'colors.whiteAlpha.100',
27 | },
28 | },
29 | _active: {
30 | [$bg.name]: 'colors.blackAlpha.200',
31 | _dark: {
32 | [$bg.name]: 'colors.whiteAlpha.200',
33 | },
34 | },
35 | _focusVisible: {
36 | boxShadow: 'outline',
37 | },
38 | bg: $bg.ref,
39 | },
40 | variants: {
41 | size: {
42 | lg: {
43 | [$size.name]: 'sizes.10',
44 | fontSize: 'md',
45 | },
46 | md: {
47 | [$size.name]: 'sizes.8',
48 | fontSize: 'xs',
49 | },
50 | sm: {
51 | [$size.name]: 'sizes.6',
52 | fontSize: '2xs',
53 | },
54 | },
55 | },
56 | defaultVariants: {
57 | size: 'md',
58 | },
59 | })
60 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/code.recipe.ts:
--------------------------------------------------------------------------------
1 | import { defineRecipe } from '@pandacss/dev'
2 | import { badgeRecipe, badgeVars } from './badge.recipe'
3 |
4 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/code.ts
5 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/layout/code.tsx
6 |
7 | export const codeRecipe = defineRecipe({
8 | className: 'code',
9 | base: {
10 | fontFamily: 'mono',
11 | fontSize: 'sm',
12 | px: '0.2em',
13 | borderRadius: 'sm',
14 | bg: badgeVars.bg.ref,
15 | color: badgeVars.color.ref,
16 | boxShadow: badgeVars.shadow.ref,
17 | },
18 | variants: badgeRecipe.variants,
19 | defaultVariants: badgeRecipe.defaultVariants,
20 | })
21 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/form-control.recipe.ts:
--------------------------------------------------------------------------------
1 | import { cssVar } from '@crepe-ui/shared'
2 | import { defineSlotRecipe } from '@pandacss/dev'
3 |
4 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/form-control.ts
5 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/form-error.ts
6 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/form-label.ts
7 |
8 | const vars = cssVar.scope('form', ['control-color', 'error-color'])
9 |
10 | export const formControlRecipe = defineSlotRecipe({
11 | className: 'form-control',
12 | slots: ['container', 'required-indicator', 'helper', 'label', 'error', 'error-icon'],
13 | base: {
14 | container: {
15 | width: '100%',
16 | position: 'relative',
17 | },
18 | 'required-indicator': {
19 | marginStart: '1',
20 | [vars['control-color'].name]: 'colors.red.500',
21 | _dark: {
22 | [vars['control-color'].name]: 'colors.red.300',
23 | },
24 | color: vars['control-color'].ref,
25 | },
26 | helper: {
27 | mt: '2',
28 | [vars['control-color'].name]: 'colors.gray.600',
29 | _dark: {
30 | [vars['control-color'].name]: 'colors.whiteAlpha.600',
31 | },
32 | color: vars['control-color'].ref,
33 | lineHeight: 'normal',
34 | fontSize: 'sm',
35 | },
36 | label: {
37 | display: 'block',
38 | textAlign: 'start',
39 | //
40 | fontSize: 'md',
41 | marginEnd: '3',
42 | mb: '2',
43 | fontWeight: 'medium',
44 | transitionProperty: 'common',
45 | transitionDuration: 'normal',
46 | opacity: 1,
47 | _disabled: {
48 | opacity: 0.4,
49 | },
50 | },
51 | error: {
52 | display: 'flex',
53 | alignItems: 'center',
54 | //
55 | [vars['error-color'].name]: `colors.red.500`,
56 | _dark: {
57 | [vars['error-color'].name]: `colors.red.300`,
58 | },
59 | color: vars['error-color'].ref,
60 | mt: '2',
61 | fontSize: 'sm',
62 | lineHeight: 'normal',
63 | },
64 | 'error-icon': {
65 | marginEnd: '0.5em',
66 | [vars['error-color'].name]: `colors.red.500`,
67 | _dark: {
68 | [vars['error-color'].name]: `colors.red.300`,
69 | },
70 | color: vars['error-color'].ref,
71 | },
72 | },
73 | })
74 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/heading.recipe.ts:
--------------------------------------------------------------------------------
1 | import { defineRecipe } from '@pandacss/dev'
2 |
3 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/heading.ts
4 |
5 | export const headingRecipe = defineRecipe({
6 | className: 'heading',
7 | base: {
8 | fontFamily: 'heading',
9 | fontWeight: 'bold',
10 | },
11 | variants: {
12 | size: {
13 | '4xl': { textStyle: 'heading.4xl' },
14 | '3xl': { textStyle: 'heading.3xl' },
15 | '2xl': { textStyle: 'heading.2xl' },
16 | xl: { textStyle: 'heading.xl' },
17 | lg: { textStyle: 'heading.lg' },
18 | md: { textStyle: 'heading.md' },
19 | sm: { textStyle: 'heading.sm' },
20 | xs: { textStyle: 'heading.xs' },
21 | },
22 | },
23 | defaultVariants: {
24 | size: 'xl',
25 | },
26 | })
27 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/icon.recipe.ts:
--------------------------------------------------------------------------------
1 | import { defineRecipe } from '@pandacss/dev'
2 |
3 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/icon/icon.tsx
4 |
5 | export const iconRecipe = defineRecipe({
6 | className: 'icon',
7 | base: {
8 | w: '1em',
9 | h: '1em',
10 | display: 'inline-block',
11 | lineHeight: '1em',
12 | flexShrink: 0,
13 | color: 'currentColor',
14 | verticalAlign: 'middle',
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/input.recipe.ts:
--------------------------------------------------------------------------------
1 | import { cssVar } from '@crepe-ui/shared'
2 | import { defineSlotRecipe } from '@pandacss/dev'
3 |
4 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/input.ts
5 | // https://github.com/chakra-ui/chakra-ui/blob/119a52932dcca3a74b469ae05dabb8bfec788dfe/packages/components/theme/src/components/input.ts
6 |
7 | const vars = cssVar.scope('input', [
8 | 'focus-border-color',
9 | 'error-border-color',
10 | 'height',
11 | 'font-size',
12 | 'padding',
13 | 'border-radius',
14 | ])
15 |
16 | const sizes = {
17 | lg: {
18 | [vars['font-size'].name]: 'fontSizes.lg',
19 | [vars.padding.name]: 'space.4',
20 | [vars['border-radius'].name]: 'radii.md',
21 | [vars.height.name]: 'sizes.12',
22 | },
23 | md: {
24 | [vars['font-size'].name]: 'fontSizes.md',
25 | [vars.padding.name]: 'space.4',
26 | [vars['border-radius'].name]: 'radii.md',
27 | [vars.height.name]: 'sizes.10',
28 | },
29 | sm: {
30 | [vars['font-size'].name]: 'fontSizes.sm',
31 | [vars.padding.name]: 'space.3',
32 | [vars['border-radius'].name]: 'radii.sm',
33 | [vars.height.name]: 'sizes.8',
34 | },
35 | xs: {
36 | [vars['font-size'].name]: 'fontSizes.xs',
37 | [vars.padding.name]: 'space.2',
38 | [vars['border-radius'].name]: 'radii.sm',
39 | [vars.height.name]: 'sizes.6',
40 | },
41 | }
42 |
43 | const withVars = {
44 | height: vars.height.ref,
45 | fontSize: vars['font-size'].ref,
46 | px: vars.padding.ref,
47 | borderRadius: vars['border-radius'].ref,
48 | }
49 |
50 | export const inputRecipe = defineSlotRecipe({
51 | className: 'input',
52 | jsx: ['Input', 'Input.Group'],
53 | slots: ['group', 'field', 'addon'],
54 | base: {
55 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/input/input-group.tsx
56 | group: {
57 | width: '100%',
58 | display: 'flex',
59 | position: 'relative',
60 | // Parts of inputs override z-index to ensure that they stack correctly on each other
61 | // Create a new stacking context so that these overrides don't leak out and conflict with other z-indexes
62 | isolation: 'isolate',
63 | //
64 | '& > [data-placement="left"]': {
65 | borderStartRadius: 0,
66 | },
67 | '& > [data-placement="right"]': {
68 | borderEndRadius: 0,
69 | },
70 | '&:has([data-placement="right"]) > input': {
71 | borderEndRadius: 0,
72 | },
73 | [vars['border-radius'].name]: '0',
74 | },
75 | addon: {
76 | ...withVars,
77 | '& + input': {
78 | borderStartRadius: 0,
79 | },
80 | },
81 | field: {
82 | ...withVars,
83 | //
84 | width: '100%',
85 | minWidth: 0,
86 | outline: 0,
87 | position: 'relative',
88 | appearance: 'none',
89 | transitionProperty: 'common',
90 | transitionDuration: 'normal',
91 | _disabled: {
92 | opacity: 0.4,
93 | cursor: 'not-allowed',
94 | },
95 | [vars['focus-border-color'].name]: {},
96 | base: {
97 | // TODO check ok
98 | [vars['focus-border-color'].name]: 'colors.blue.500',
99 | [vars['error-border-color'].name]: 'colors.red.500',
100 | },
101 | _dark: {
102 | [vars['focus-border-color'].name]: 'colors.blue.300',
103 | [vars['error-border-color'].name]: 'colors.red.300',
104 | },
105 | },
106 | },
107 | variants: {
108 | size: {
109 | lg: { field: sizes.lg, addon: sizes.lg, group: sizes.lg },
110 | md: { field: sizes.md, addon: sizes.md, group: sizes.md },
111 | sm: { field: sizes.sm, addon: sizes.sm, group: sizes.sm },
112 | xs: { field: sizes.xs, addon: sizes.xs, group: sizes.xs },
113 | },
114 | variant: {
115 | outline: {
116 | field: {
117 | border: '1px solid',
118 | borderColor: 'inherit',
119 | bg: 'inherit',
120 | _hover: {
121 | borderColor: { base: 'gray.300', _dark: 'whiteAlpha.400' },
122 | },
123 | _readOnly: {
124 | boxShadow: 'none !important',
125 | userSelect: 'all',
126 | },
127 | _invalid: {
128 | borderColor: vars['error-border-color'].ref,
129 | boxShadow: `0 0 0 1px ${vars['error-border-color'].ref}`,
130 | },
131 | _focusVisible: {
132 | zIndex: 1,
133 | borderColor: vars['focus-border-color'].ref,
134 | boxShadow: `0 0 0 1px ${vars['focus-border-color'].ref}`,
135 | },
136 | },
137 | addon: {
138 | border: '1px solid',
139 | borderColor: { base: 'inherit', _dark: 'whiteAlpha.50' },
140 | bg: { base: 'gray.100', _dark: 'whiteAlpha.300' },
141 | },
142 | },
143 | filled: {
144 | field: {
145 | border: '2px solid',
146 | borderColor: 'transparent',
147 | bg: { base: 'gray.100', _dark: 'whiteAlpha.50' },
148 | _hover: {
149 | bg: { base: 'gray.200', _dark: 'whiteAlpha.100' },
150 | },
151 | _readOnly: {
152 | boxShadow: 'none !important',
153 | userSelect: 'all',
154 | },
155 | _invalid: {
156 | borderColor: vars['error-border-color'].ref,
157 | },
158 | _focusVisible: {
159 | bg: 'transparent',
160 | borderColor: vars['focus-border-color'].ref,
161 | },
162 | },
163 | addon: {
164 | border: '2px solid',
165 | borderColor: 'transparent',
166 | bg: { base: 'gray.100', _dark: 'whiteAlpha.50' },
167 | },
168 | },
169 | flushed: {
170 | field: {
171 | borderBottom: '1px solid',
172 | borderColor: 'inherit',
173 | borderRadius: '0',
174 | px: '0',
175 | bg: 'transparent',
176 | _readOnly: {
177 | boxShadow: 'none !important',
178 | userSelect: 'all',
179 | },
180 | _invalid: {
181 | borderColor: vars['error-border-color'].ref,
182 | boxShadow: `0px 1px 0px 0px ${vars['error-border-color'].ref}`,
183 | },
184 | _focusVisible: {
185 | borderColor: vars['focus-border-color'].ref,
186 | boxShadow: `0px 1px 0px 0px ${vars['focus-border-color'].ref}`,
187 | },
188 | },
189 | addon: {
190 | borderBottom: '2px solid',
191 | borderColor: 'inherit',
192 | borderRadius: '0',
193 | px: '0',
194 | bg: 'transparent',
195 | },
196 | },
197 | unstyled: {
198 | field: {
199 | bg: 'transparent',
200 | px: '0',
201 | height: 'auto',
202 | },
203 | addon: {
204 | bg: 'transparent',
205 | px: '0',
206 | height: 'auto',
207 | },
208 | },
209 | },
210 | },
211 | defaultVariants: {
212 | size: 'md',
213 | variant: 'outline',
214 | },
215 | })
216 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/kbd.recipe.ts:
--------------------------------------------------------------------------------
1 | import { defineRecipe } from '@pandacss/dev'
2 | import { cssVar } from '@crepe-ui/shared'
3 |
4 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/kbd.ts
5 |
6 | const $bg = cssVar.create('kbd-bg')
7 |
8 | export const kbdRecipe = defineRecipe({
9 | className: 'kbd',
10 | base: {
11 | [$bg.name]: 'colors.gray.100',
12 | _dark: {
13 | [$bg.name]: 'colors.whiteAlpha.100',
14 | },
15 | bg: $bg.ref,
16 | borderRadius: 'md',
17 | borderWidth: '1px',
18 | borderBottomWidth: '3px',
19 | fontSize: '0.8em',
20 | fontWeight: 'bold',
21 | lineHeight: 'normal',
22 | px: '0.4em',
23 | whiteSpace: 'nowrap',
24 | },
25 | })
26 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/link.recipe.ts:
--------------------------------------------------------------------------------
1 | import { defineRecipe } from '@pandacss/dev'
2 |
3 | export const linkRecipe = defineRecipe({
4 | className: 'link',
5 | base: {
6 | transitionProperty: 'common',
7 | transitionDuration: 'fast',
8 | transitionTimingFunction: 'ease-out',
9 | cursor: 'pointer',
10 | textDecoration: 'none',
11 | outline: 'none',
12 | color: 'inherit',
13 | _hover: {
14 | textDecoration: 'underline',
15 | },
16 | _focusVisible: {
17 | boxShadow: 'outline',
18 | },
19 | },
20 | })
21 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/modal.recipe.ts:
--------------------------------------------------------------------------------
1 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/modal.ts
2 |
3 | import { dialogAnatomy } from '@ark-ui/anatomy'
4 |
5 | import { defineSlotRecipe } from '@pandacss/dev'
6 | import { cssVar } from '@crepe-ui/shared'
7 |
8 | const $bg = cssVar.create('modal-bg')
9 | const $shadow = cssVar.create('modal-shadow')
10 |
11 | /**
12 | * Since the `maxWidth` prop references theme.sizes internally,
13 | * we can leverage that to size our modals.
14 | */
15 | function getSize(value: string) {
16 | if (value === 'full') {
17 | return {
18 | content: {
19 | maxW: '100vw',
20 | minH: '100vh',
21 | my: '0',
22 | borderRadius: '0',
23 | },
24 | }
25 | }
26 | return {
27 | content: { maxW: value },
28 | }
29 | }
30 |
31 | /**
32 | * chakra v3 parts - ark-ui slots
33 | *
34 | * @prop content = dialog
35 | * @prop overlay = backdrop
36 | * @prop close = closeTrigger
37 | * @prop dialogContainer = container
38 | * @prop body = description
39 | * @prop header = title
40 | * @prop trigger = trigger
41 | */
42 | export const modalRecipe = defineSlotRecipe({
43 | className: 'modal',
44 | slots: dialogAnatomy.extendWith('root', 'footer').keys(),
45 | base: {
46 | backdrop: {
47 | bg: 'blackAlpha.600',
48 | zIndex: 'modal',
49 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/modal/modal-overlay.tsx
50 | pos: 'fixed',
51 | left: '0',
52 | top: '0',
53 | w: '100vw',
54 | h: '100vh',
55 | },
56 | container: {
57 | display: 'flex',
58 | zIndex: 'modal',
59 | justifyContent: 'center',
60 | overscrollBehaviorY: 'none',
61 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/modal/modal-content.tsx
62 | width: '100vw',
63 | height: '100vh',
64 | position: 'fixed',
65 | left: 0,
66 | top: 0,
67 | },
68 | content: {
69 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/modal/modal-content.tsx
70 | display: 'flex',
71 | flexDirection: 'column',
72 | position: 'relative',
73 | width: '100%',
74 | outline: 0,
75 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/modal.ts#L36
76 | borderRadius: 'md',
77 | color: 'inherit',
78 | zIndex: 'modal',
79 | [$bg.name]: 'colors.white', // TODO check
80 | [$shadow.name]: 'shadows.lg',
81 | _dark: {
82 | [$bg.name]: 'colors.gray.700',
83 | [$shadow.name]: 'shadows.dark-lg',
84 | },
85 | bg: $bg.ref,
86 | boxShadow: $shadow.ref,
87 | px: '6',
88 | py: '2',
89 | flex: '1',
90 | },
91 | description: {
92 | px: '6',
93 | py: '2',
94 | flex: '1',
95 | },
96 | title: {
97 | px: '6',
98 | py: '4',
99 | fontSize: 'xl',
100 | fontWeight: 'semibold',
101 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/modal/modal-header.tsx
102 | flex: 0,
103 | },
104 | closeTrigger: {
105 | position: 'absolute',
106 | top: '2',
107 | insetEnd: '3',
108 | },
109 | footer: {
110 | px: '6',
111 | py: '4',
112 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/modal/modal-footer.tsx
113 | display: 'flex',
114 | alignItems: 'center',
115 | justifyContent: 'flex-end',
116 | },
117 | },
118 | variants: {
119 | size: {
120 | xs: getSize('xs'),
121 | sm: getSize('sm'),
122 | md: getSize('md'),
123 | lg: getSize('lg'),
124 | xl: getSize('xl'),
125 | '2xl': getSize('2xl'),
126 | '3xl': getSize('3xl'),
127 | '4xl': getSize('4xl'),
128 | '5xl': getSize('5xl'),
129 | '6xl': getSize('6xl'),
130 | full: getSize('full'),
131 | },
132 | isCentered: {
133 | true: {
134 | content: {
135 | my: 'auto',
136 | mx: 'auto',
137 | },
138 | container: {
139 | alignItems: 'center',
140 | },
141 | },
142 | false: {
143 | content: {
144 | my: '16',
145 | mx: undefined,
146 | },
147 | container: {
148 | alignItems: 'flex-start',
149 | },
150 | },
151 | },
152 | scrollOverflowBehavior: {
153 | inside: {
154 | content: {
155 | maxH: 'calc(100% - 7.5rem)',
156 | },
157 | container: {
158 | overflow: 'hidden',
159 | },
160 | body: {
161 | overflow: 'auto',
162 | },
163 | },
164 | outside: {
165 | content: {
166 | maxH: undefined, // TODO unset ?
167 | },
168 | container: {
169 | overflow: 'auto',
170 | },
171 | body: {
172 | overflow: undefined, // TODO unset ?
173 | },
174 | },
175 | },
176 | },
177 | // https://chakra-ui.com/docs/components/modal/props
178 | defaultVariants: {
179 | size: 'md',
180 | isCentered: false,
181 | scrollOverflowBehavior: 'outside',
182 | },
183 | })
184 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/popover.recipe.ts:
--------------------------------------------------------------------------------
1 | import { popoverAnatomy } from '@ark-ui/anatomy'
2 | import { cssVar } from '@crepe-ui/shared'
3 | import { defineSlotRecipe } from '@pandacss/dev'
4 |
5 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/popover.ts
6 |
7 | const vars = cssVar.scope('popper', ['bg', 'arrow-bg', 'arrow-shadow-color'])
8 | const arrow = cssVar.scope('arrow', ['size', 'size-half', 'background', 'offset'])
9 |
10 | /**
11 | * chakra v3 parts - ark-ui slots
12 | *
13 | * @prop popper = positioner
14 | * @prop content = content
15 | * @prop header = title
16 | * @prop body = description
17 | * @prop trigger = trigger
18 | * @prop closeButton = closeTrigger
19 | * @prop arrow = arrow
20 | * @prop - = arrowTip
21 | * @prop - = anchor
22 | */
23 | export const popoverRecipe = defineSlotRecipe({
24 | className: 'popover',
25 | slots: popoverAnatomy.extendWith('footer').keys(),
26 | base: {
27 | positioner: {
28 | position: 'relative',
29 | zIndex: 'popover',
30 | },
31 | content: {
32 | position: 'relative',
33 | display: 'flex',
34 | flexDirection: 'column',
35 | //
36 | [vars.bg.name]: `colors.white`,
37 | bg: vars.bg.ref,
38 | [vars['arrow-bg'].name]: vars.bg.ref,
39 | [vars['arrow-shadow-color'].name]: `colors.gray.200`,
40 | _dark: {
41 | [vars.bg.name]: `colors.grayDark.800`,
42 | [vars['arrow-shadow-color'].name]: `colors.whiteAlpha.300`,
43 | },
44 | width: 'xs',
45 | border: '1px solid',
46 | borderColor: 'inherit',
47 | borderRadius: 'md',
48 | boxShadow: 'sm',
49 | zIndex: 'inherit',
50 | _focusVisible: {
51 | outline: 0,
52 | boxShadow: 'outline',
53 | },
54 | // |
55 | // v from Park-UI
56 | _open: {
57 | animation: 'fade-in 0.25s ease-out',
58 | },
59 | _closed: {
60 | animation: 'fade-out 0.2s ease-out',
61 | },
62 | },
63 | title: {
64 | px: 3,
65 | py: 2,
66 | borderBottomWidth: '1px',
67 | },
68 | description: {
69 | px: 3,
70 | py: 2,
71 | },
72 | footer: {
73 | px: 3,
74 | py: 2,
75 | borderTopWidth: '1px',
76 | },
77 | closeTrigger: {
78 | position: 'absolute',
79 | borderRadius: 'md',
80 | top: 1,
81 | insetEnd: 2,
82 | padding: 2,
83 | },
84 | arrow: {
85 | // this is from Ark-UI/ZagJS
86 | // https://github.com/chakra-ui/zag/blob/9a3a82f0b3738beda59c313fafd51360e6b0322f/website/data/components/popover.mdx#L246
87 | // https://github.com/chakra-ui/zag/blob/3765f9d6e75465475a4823a3b36d771796fc901a/packages/utilities/popper/src/middleware.ts#L10
88 | // https://github.com/chakra-ui/zag/blob/9a3a82f0b3738beda59c313fafd51360e6b0322f/website/data/components/tooltip.mdx#L261
89 | [arrow.size.name]: 'sizes.3',
90 | [arrow.background.name]: vars['arrow-bg'].ref,
91 | },
92 | arrowTip: {
93 | borderTopWidth: '1px',
94 | borderLeftWidth: '1px',
95 | },
96 | },
97 | })
98 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/select.recipe.ts:
--------------------------------------------------------------------------------
1 | import { selectAnatomy } from '@ark-ui/anatomy'
2 | import { defineSlotRecipe } from '@pandacss/dev'
3 | import { inputRecipe } from './input.recipe'
4 | import { cssVar } from '@crepe-ui/shared'
5 |
6 | // most of this recipe is from Park-UI as the Chakra-UI select is very different
7 | // Chakra's select is a native select styled like a Chakra Input
8 | // Whereas Park-UI's (which is from Ark-UI) select is a custom select with a custom dropdown
9 | // slight tweaks, used borderRadius md instead of sm
10 | // I removed the Park-UI semantic token usage tho (things like fg.muted, border.accent, bg.subtle etc)
11 |
12 | // https://github.com/cschroeter/park-ui/blob/1320a779d8ec2e6b6a0f9eeb9a9658aef4e2ea6f/packages/presets/src/theme/recipes/select.ts#L4
13 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/select.ts
14 |
15 | const vars = cssVar.scope('select', ['bg'])
16 | const $bg = vars.bg
17 |
18 | export const selectRecipe = defineSlotRecipe({
19 | className: 'select',
20 | description: 'A select style',
21 | slots: selectAnatomy.keys(),
22 | base: {
23 | root: {
24 | colorPalette: 'gray',
25 | display: 'flex',
26 | flexDirection: 'column',
27 | gap: '1.5',
28 | width: 'full',
29 | },
30 | content: {
31 | // also need this one since the content will be inside a Portal
32 | colorPalette: 'gray',
33 | bg: { base: 'white', _dark: 'colorPalette.900' },
34 | borderRadius: 'md',
35 | borderWidth: '1px',
36 | boxShadow: 'sm',
37 | display: 'flex',
38 | flexDirection: 'column',
39 | _hidden: {
40 | display: 'none',
41 | },
42 | _open: {
43 | animation: 'fade-in 0.25s ease-out',
44 | },
45 | _closed: {
46 | animation: 'fade-out 0.2s ease-out',
47 | },
48 | _focusVisible: {
49 | outlineOffset: '2px',
50 | outline: '2px solid',
51 | outlineColor: { base: 'colorPalette.600', _dark: 'colorPalette.400' },
52 | },
53 | },
54 | item: {
55 | alignItems: 'center',
56 | borderRadius: 'xs',
57 | cursor: 'pointer',
58 | display: 'flex',
59 | justifyContent: 'space-between',
60 | transitionDuration: 'fast',
61 | transitionProperty: 'background, color',
62 | transitionTimingFunction: 'default',
63 | _hover: {
64 | background: { base: 'colorPalette.100', _dark: 'colorPalette.800' },
65 | },
66 | _highlighted: {
67 | background: { base: 'colorPalette.100', _dark: 'colorPalette.800' },
68 | },
69 | _disabled: {
70 | colorAlpha: { base: 'gray.300', _dark: 'gray.300/50' },
71 | cursor: 'not-allowed',
72 | _hover: {
73 | background: 'transparent',
74 | },
75 | },
76 | },
77 | itemGroupLabel: {
78 | fontWeight: 'semibold',
79 | textStyle: 'sm',
80 | },
81 | itemIndicator: {
82 | color: 'accent.default',
83 | },
84 | label: {
85 | color: { base: 'colorPalette.700', _dark: 'colorPalette.200' },
86 | fontWeight: 'medium',
87 | },
88 | trigger: {
89 | // the only part that is from Chakra-UI
90 | ...inputRecipe.base?.field,
91 | appearance: 'none',
92 | paddingBottom: '1px',
93 | lineHeight: 'normal',
94 | bg: $bg.ref,
95 | [$bg.name]: 'colors.white',
96 | _dark: {
97 | [$bg.name]: 'colors.colorPalette.700',
98 | },
99 | alignItems: 'center',
100 | borderColor: { base: 'colorPalette.300', _dark: 'colorPalette.700' },
101 | borderRadius: 'md',
102 | cursor: 'pointer',
103 | display: 'inline-flex',
104 | justifyContent: 'space-between',
105 | outline: 0,
106 | position: 'relative',
107 | transitionDuration: 'normal',
108 | transitionProperty: 'background, box-shadow, border-color',
109 | transitionTimingFunction: 'default',
110 | width: 'full',
111 | _placeholderShown: {
112 | color: { base: 'colorPalette.500', _dark: 'colorPalette.400' },
113 | },
114 | '& :where(svg)': {
115 | color: { base: 'colorPalette.500', _dark: 'colorPalette.400' },
116 | },
117 | },
118 | },
119 | variants: {
120 | variant: {
121 | outline: {
122 | trigger: {
123 | bg: { base: 'white', _dark: 'colorPalette.800' },
124 | borderWidth: '1px',
125 | _focus: {
126 | borderColor: {
127 | base: 'colorPalette.200',
128 | _dark: 'colorPalette.800',
129 | },
130 | boxShadow: 'accent',
131 | },
132 | },
133 | },
134 | ghost: {
135 | trigger: {
136 | _hover: {
137 | background: { base: 'colorPalette.100', _dark: 'colorPalette.900' },
138 | },
139 | _focus: {
140 | background: { base: 'colorPalette.100', _dark: 'colorPalette.900' },
141 | },
142 | },
143 | },
144 | },
145 | size: {
146 | sm: {
147 | content: { p: '0.5', gap: '1' },
148 | item: { textStyle: 'sm', px: '2', height: '9' },
149 | itemIndicator: {
150 | '& :where(svg)': {
151 | width: '4',
152 | height: '4',
153 | },
154 | },
155 | itemGroupLabel: {
156 | px: '2',
157 | py: '1.5',
158 | },
159 |
160 | label: { textStyle: 'sm' },
161 | trigger: {
162 | px: '2.5',
163 | h: '9',
164 | minW: '9',
165 | fontSize: 'sm',
166 | gap: '2',
167 | '& :where(svg)': {
168 | width: '4',
169 | height: '4',
170 | },
171 | },
172 | },
173 | md: {
174 | content: { p: '1', gap: '1' },
175 | item: { textStyle: 'md', px: '2', height: '10' },
176 | itemIndicator: {
177 | '& :where(svg)': {
178 | width: '4',
179 | height: '4',
180 | },
181 | },
182 | itemGroupLabel: {
183 | px: '2',
184 | py: '1.5',
185 | },
186 | label: { textStyle: 'sm' },
187 | trigger: {
188 | px: '3',
189 | h: '10',
190 | minW: '10',
191 | fontSize: 'md',
192 | gap: '2',
193 | '& :where(svg)': {
194 | width: '4',
195 | height: '4',
196 | },
197 | },
198 | },
199 | lg: {
200 | content: { p: '1.5', gap: '1' },
201 | item: { textStyle: 'md', px: '2', height: '11' },
202 | itemIndicator: {
203 | '& :where(svg)': {
204 | width: '5',
205 | height: '5',
206 | },
207 | },
208 | itemGroupLabel: {
209 | px: '2',
210 | py: '1.5',
211 | },
212 | label: { textStyle: 'sm' },
213 | trigger: {
214 | px: '3.5',
215 | h: '11',
216 | minW: '11',
217 | fontSize: 'md',
218 | gap: '2',
219 | '& :where(svg)': {
220 | width: '5',
221 | height: '5',
222 | },
223 | },
224 | },
225 | },
226 | },
227 | defaultVariants: {
228 | size: 'md',
229 | variant: 'outline',
230 | },
231 | })
232 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/skeleton.recipe.tsx:
--------------------------------------------------------------------------------
1 | import { defineRecipe } from "@pandacss/dev";
2 | import { cssVar } from "@crepe-ui/shared";
3 |
4 | import { cssVariables } from "../vars";
5 |
6 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/skeleton.ts
7 | // https://github.com/chakra-ui/chakra-ui/blob/b904bbccab7d9c3b7ead48043b0e0652701f31f8/packages/components/src/skeleton/skeleton.tsx
8 |
9 | const $speed = cssVar.create("skeleton-speed", "0.8s");
10 |
11 | export const skeletonRecipe = defineRecipe({
12 | className: "skeleton",
13 | base: {
14 | // https://github.com/chakra-ui/chakra-ui-docs/blob/7e0869341db671880ea08ec74b84a93817f32845/configs/sandpack-contents/component-theming/skeleton.js#L116
15 | h: "20px",
16 | //
17 | [cssVariables.skeleton["start-color"].name]: "colors.gray.100",
18 | [cssVariables.skeleton["end-color"].name]: "colors.gray.400",
19 | _dark: {
20 | [cssVariables.skeleton["start-color"].name]: "colors.gray.800",
21 | [cssVariables.skeleton["end-color"].name]: "colors.gray.600",
22 | },
23 | background: cssVariables.skeleton["start-color"].ref,
24 | borderColor: cssVariables.skeleton["end-color"].ref,
25 | opacity: 0.7,
26 | borderRadius: "sm",
27 | //
28 | boxShadow: "none",
29 | backgroundClip: "padding-box",
30 | cursor: "default",
31 | pointerEvents: "none",
32 | userSelect: "none",
33 | "&::before, &::after, *": {
34 | visibility: "hidden",
35 | },
36 | //
37 | animation: `${$speed.ref} linear infinite alternate skeleton-bg-fade`,
38 | },
39 | });
40 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/switch.recipe.tsx:
--------------------------------------------------------------------------------
1 | import { cssVar, calc } from "@crepe-ui/shared";
2 | import { defineSlotRecipe } from "@pandacss/dev";
3 | import { switchAnatomy } from "@ark-ui/anatomy";
4 |
5 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/switch.ts
6 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/switch/switch.tsx
7 |
8 | const vars = cssVar.scope("switch", [
9 | "track-width",
10 | "track-height",
11 | "track-diff",
12 | "thumb-x",
13 | "bg",
14 | ]);
15 | const diffValue = calc.subtract(vars["track-width"], vars["track-height"]);
16 |
17 | /**
18 | * chakra v3 parts - ark-ui slots
19 | *
20 | * @prop container = root
21 | * @prop track = control
22 | * @prop thumb = thumb
23 | * @prop input = input
24 | */
25 | export const switchRecipe = defineSlotRecipe({
26 | className: "switch",
27 | slots: switchAnatomy.keys(),
28 | jsx: ["Switch"],
29 | base: {
30 | root: {
31 | colorPalette: "blue",
32 | [vars["track-diff"].name]: diffValue,
33 | [vars["thumb-x"].name]: vars["track-diff"].ref,
34 | _rtl: {
35 | [vars["thumb-x"].name]: calc(vars["track-diff"]).negate().toString(),
36 | },
37 | //
38 | position: "relative",
39 | verticalAlign: "middle",
40 | lineHeight: 0,
41 | //
42 | display: "inline-flex",
43 | alignItems: "center",
44 | },
45 | control: {
46 | borderRadius: "full",
47 | p: "0.5",
48 | width: vars["track-width"].ref,
49 | height: vars["track-height"].ref,
50 | transitionProperty: "background, transform",
51 | transitionDuration: "fast",
52 | [vars.bg.name]: "colors.gray.300",
53 | _dark: {
54 | [vars.bg.name]: "colors.whiteAlpha.400",
55 | },
56 | _focusVisible: {
57 | boxShadow: "outline",
58 | },
59 | _disabled: {
60 | opacity: 0.4,
61 | cursor: "not-allowed",
62 | },
63 | _checked: {
64 | [vars.bg.name]: `colors.colorPalette.500`,
65 | _dark: {
66 | [vars.bg.name]: `colors.colorPalette.200`,
67 | },
68 | },
69 | bg: vars.bg.ref,
70 | //
71 | display: "inline-flex",
72 | flexShrink: 0,
73 | justifyContent: "flex-start",
74 | boxSizing: "content-box",
75 | cursor: "pointer",
76 | },
77 | thumb: {
78 | bg: "white",
79 | transitionProperty: "transform",
80 | transitionDuration: "normal",
81 | borderRadius: "inherit",
82 | width: vars["track-height"].ref,
83 | height: vars["track-height"].ref,
84 | _checked: {
85 | transform: `translateX(${vars["thumb-x"].ref})`,
86 | },
87 | },
88 | label: {
89 | userSelect: "none",
90 | marginStart: "0.5rem",
91 | },
92 | },
93 | variants: {
94 | size: {
95 | sm: {
96 | root: {
97 | [vars["track-width"].name]: "1.375rem",
98 | [vars["track-height"].name]: "sizes.3",
99 | },
100 | },
101 | md: {
102 | root: {
103 | [vars["track-width"].name]: "1.875rem",
104 | [vars["track-height"].name]: "sizes.4",
105 | },
106 | },
107 | lg: {
108 | root: {
109 | [vars["track-width"].name]: "2.875rem",
110 | [vars["track-height"].name]: "sizes.6",
111 | },
112 | },
113 | },
114 | },
115 | defaultVariants: {
116 | size: "md",
117 | },
118 | });
119 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/table.recipe.ts:
--------------------------------------------------------------------------------
1 | import { defineSlotRecipe, defineStyles } from '@pandacss/dev'
2 |
3 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/table.ts
4 |
5 | const numericStyles = defineStyles({
6 | '&[data-is-numeric=true]': {
7 | textAlign: 'end',
8 | },
9 | })
10 |
11 | export const tableRecipe = defineSlotRecipe({
12 | className: 'table',
13 | jsx: ['Table', 'Table.Container', 'Table.Root'],
14 | slots: ['container', 'table', 'caption', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr'],
15 | base: {
16 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/table/table-container.tsx
17 | container: {
18 | display: 'block',
19 | whiteSpace: 'nowrap',
20 | WebkitOverflowScrolling: 'touch',
21 | overflowX: 'auto',
22 | overflowY: 'hidden',
23 | maxWidth: '100%',
24 | },
25 | table: {
26 | fontVariantNumeric: 'lining-nums tabular-nums',
27 | borderCollapse: 'collapse',
28 | width: 'full',
29 | //
30 | colorPalette: 'gray',
31 | },
32 | th: {
33 | fontFamily: 'heading',
34 | fontWeight: 'bold',
35 | textTransform: 'uppercase',
36 | letterSpacing: 'wider',
37 | textAlign: 'start',
38 | },
39 | td: {
40 | textAlign: 'start',
41 | },
42 | caption: {
43 | mt: 4,
44 | fontFamily: 'heading',
45 | textAlign: 'center',
46 | fontWeight: 'medium',
47 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/table/table-caption.tsx
48 | captionSide: 'bottom',
49 | },
50 | },
51 | variants: {
52 | variant: {
53 | simple: {
54 | th: {
55 | color: { base: 'gray.600', _dark: 'gray.400' },
56 | borderBottom: '1px',
57 | borderColor: { base: `colorPalette.100`, _dark: `colorPalette.700` },
58 | ...numericStyles,
59 | },
60 | td: {
61 | borderBottom: '1px',
62 | borderColor: { base: `colorPalette.100`, _dark: `colorPalette.700` },
63 | ...numericStyles,
64 | },
65 | caption: {
66 | color: { base: 'gray.600', _dark: 'gray.100' },
67 | },
68 | },
69 | tfoot: {
70 | tr: {
71 | '&:last-of-type': {
72 | th: { borderBottomWidth: 0 },
73 | },
74 | },
75 | },
76 | striped: {
77 | th: {
78 | color: { base: 'gray.600', _dark: 'gray.400' },
79 | borderBottom: '1px',
80 | borderColor: { base: `colorPalette.100`, _dark: `colorPalette.700` },
81 | ...numericStyles,
82 | },
83 | td: {
84 | borderBottom: '1px',
85 | borderColor: { base: `colorPalette.100`, _dark: `colorPalette.700` },
86 | ...numericStyles,
87 | },
88 | caption: {
89 | color: { base: 'gray.600', _dark: 'gray.100' },
90 | },
91 | tbody: {
92 | '& tr:nth-of-type(odd)': {
93 | '& th, & td': {
94 | borderBottomWidth: '1px',
95 | borderColor: {
96 | base: `colorPalette.100`,
97 | _dark: `colorPalette.700`,
98 | },
99 | },
100 | '& td': {
101 | background: {
102 | base: `colorPalette.100`,
103 | _dark: `colorPalette.700`,
104 | },
105 | },
106 | },
107 | },
108 | tfoot: {
109 | '& tr:last-of-type': {
110 | th: { borderBottomWidth: 0 },
111 | },
112 | },
113 | },
114 | unstyled: {},
115 | },
116 | size: {
117 | sm: {
118 | th: {
119 | px: '4',
120 | py: '1',
121 | lineHeight: '4',
122 | fontSize: 'xs',
123 | },
124 | td: {
125 | px: '4',
126 | py: '2',
127 | fontSize: 'sm',
128 | lineHeight: '4',
129 | },
130 | caption: {
131 | px: '4',
132 | py: '2',
133 | fontSize: 'xs',
134 | },
135 | },
136 | md: {
137 | th: {
138 | px: '6',
139 | py: '3',
140 | lineHeight: '4',
141 | fontSize: 'xs',
142 | },
143 | td: {
144 | px: '6',
145 | py: '4',
146 | lineHeight: '5',
147 | },
148 | caption: {
149 | px: '6',
150 | py: '2',
151 | fontSize: 'sm',
152 | },
153 | },
154 | lg: {
155 | th: {
156 | px: '8',
157 | py: '4',
158 | lineHeight: '5',
159 | fontSize: 'sm',
160 | },
161 | td: {
162 | px: '8',
163 | py: '5',
164 | lineHeight: '6',
165 | },
166 | caption: {
167 | px: '6',
168 | py: '2',
169 | fontSize: 'md',
170 | },
171 | },
172 | },
173 | },
174 | defaultVariants: {
175 | variant: 'simple',
176 | size: 'md',
177 | },
178 | })
179 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/tabs.recipe.ts:
--------------------------------------------------------------------------------
1 | import { defineSlotRecipe } from '@pandacss/dev'
2 | import { tabsAnatomy } from '@ark-ui/anatomy'
3 | import { cssVar } from '@crepe-ui/shared'
4 |
5 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/tabs.ts
6 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/tabs/tab.tsx
7 |
8 | const vars = cssVar.scope('tabs', ['color', 'bg', 'border-color', 'border-prop', 'margin-prop'])
9 | const $fg = vars.color
10 | const $bg = vars.bg
11 | const $border = vars['border-color']
12 |
13 | /**
14 | * chakra v3 parts - ark-ui slots
15 | *
16 | * @prop root - root
17 | * @prop tablist - list
18 | * @prop tab - trigger
19 | * @prop tabpanels - content
20 | * @prop tabpanel - .
21 | * @prop . - indicator
22 | */
23 | export const tabsRecipe = defineSlotRecipe({
24 | className: 'tabs',
25 | slots: tabsAnatomy.extendWith('panels').keys(),
26 | base: {
27 | root: {
28 | colorPalette: 'blue',
29 | _vertical: { display: 'flex' },
30 | _horizontal: { display: 'block' },
31 | },
32 | list: {
33 | // from Park-UI
34 | position: 'relative',
35 | scrollbarWidth: 'none',
36 | '&::-webkit-scrollbar': {
37 | display: 'none',
38 | },
39 | //
40 | display: 'flex',
41 | _vertical: {
42 | flexDirection: 'column',
43 | borderInlineStart: vars['border-color'].ref,
44 | marginInlineStart: vars['margin-prop'].ref,
45 | },
46 | _horizontal: {
47 | flexDirection: 'row',
48 | borderBottom: vars['border-color'].ref,
49 | marginBottom: vars['margin-prop'].ref,
50 | },
51 | },
52 | trigger: {
53 | outline: '0',
54 | display: 'flex',
55 | alignItems: 'center',
56 | justifyContent: 'center',
57 | //
58 | transitionProperty: 'common',
59 | transitionDuration: 'normal',
60 | _focusVisible: {
61 | zIndex: 1,
62 | boxShadow: 'outline',
63 | },
64 | _disabled: {
65 | cursor: 'not-allowed',
66 | opacity: 0.4,
67 | },
68 | },
69 | panels: {
70 | width: '100%',
71 | },
72 | content: {
73 | outline: '0',
74 | p: 4,
75 | },
76 | // https://github.com/chakra-ui/chakra-ui/blob/cb71ce45ca82765c11e6d45d627303ddb60ea23a/packages/components/src/tabs/use-tabs.ts
77 | indicator: {
78 | display: 'none', // there's no indicator in Chakra-UI
79 | //
80 | bgColor: 'colorPalette.950',
81 | _horizontal: {
82 | height: '2px',
83 | bottom: '0',
84 | },
85 | _vertical: {
86 | width: '2px',
87 | left: '0',
88 | },
89 | },
90 | },
91 | variants: {
92 | size: {
93 | sm: { trigger: { py: 1, px: 4, fontSize: 'sm' } },
94 | md: { trigger: { fontSize: 'md', py: 2, px: 4 } },
95 | lg: { trigger: { fontSize: 'lg', py: 3, px: 4 } },
96 | },
97 | variant: {
98 | line: {
99 | list: {
100 | [vars['border-color'].name]: '2px solid',
101 | borderColor: 'inherit',
102 | },
103 | trigger: {
104 | [vars['border-color'].name]: '2px solid',
105 | borderColor: 'transparent',
106 | [vars['margin-prop'].name]: '-2px',
107 | _selected: {
108 | [$fg.name]: `colors.colorPalette.600`,
109 | _dark: {
110 | [$fg.name]: `colors.colorPalette.300`,
111 | },
112 | borderColor: 'currentColor',
113 | },
114 | _active: {
115 | [$bg.name]: 'colors.gray.200',
116 | _dark: {
117 | [$bg.name]: 'colors.whiteAlpha.300',
118 | },
119 | },
120 | _disabled: {
121 | _active: { bg: 'none' },
122 | },
123 | color: $fg.ref,
124 | bg: $bg.ref,
125 | },
126 | },
127 | enclosed: {
128 | trigger: {
129 | borderTopRadius: 'md',
130 | border: '1px solid',
131 | borderColor: 'transparent',
132 | mb: '-1px',
133 | [$border.name]: 'transparent',
134 | _selected: {
135 | [$fg.name]: `colors.colorPalette.600`,
136 | [$border.name]: `colors.white`,
137 | _dark: {
138 | [$fg.name]: `colors.colorPalette.300`,
139 | [$border.name]: `colors.gray.800`,
140 | },
141 | borderColor: 'inherit',
142 | borderBottomColor: $border.ref,
143 | },
144 | color: $fg.ref,
145 | },
146 | list: {
147 | mb: '-1px',
148 | borderBottom: '1px solid',
149 | borderColor: 'inherit',
150 | },
151 | },
152 | 'enclosed-colored': {
153 | trigger: {
154 | border: '1px solid',
155 | borderColor: 'inherit',
156 | [$bg.name]: 'colors.gray.50',
157 | _dark: {
158 | [$bg.name]: 'colors.whiteAlpha.50',
159 | },
160 | mb: '-1px',
161 | _notLast: {
162 | marginEnd: '-1px',
163 | },
164 | _selected: {
165 | [$bg.name]: 'colors.white',
166 | [$fg.name]: `colors.colorPalette.600`,
167 | _dark: {
168 | [$bg.name]: 'colors.gray.800',
169 | [$fg.name]: `colors.colorPalette.300`,
170 | },
171 | borderColor: 'inherit',
172 | borderTopColor: 'currentColor',
173 | borderBottomColor: 'transparent',
174 | },
175 | color: $fg.ref,
176 | bg: $bg.ref,
177 | },
178 | list: {
179 | mb: '-1px',
180 | borderBottom: '1px solid',
181 | borderColor: 'inherit',
182 | },
183 | },
184 | 'soft-rounded': {
185 | trigger: {
186 | borderRadius: 'full',
187 | fontWeight: 'semibold',
188 | color: 'gray.600',
189 | _selected: {
190 | color: `colorPalette.700`,
191 | bg: `colorPalette.100`,
192 | },
193 | },
194 | },
195 | 'solid-rounded': {
196 | trigger: {
197 | borderRadius: 'full',
198 | fontWeight: 'semibold',
199 | [$fg.name]: 'colors.gray.600',
200 | _dark: {
201 | [$fg.name]: 'inherit',
202 | },
203 | _selected: {
204 | [$fg.name]: 'colors.white',
205 | [$bg.name]: `colors.colorPalette.600`,
206 | _dark: {
207 | [$fg.name]: 'colors.gray.800',
208 | [$bg.name]: `colors.colorPalette.300`,
209 | },
210 | },
211 | color: $fg.ref,
212 | bg: $bg.ref,
213 | },
214 | },
215 | unstyled: {},
216 | },
217 | isFitted: {
218 | true: {
219 | trigger: { flex: 1 },
220 | },
221 | },
222 | },
223 | defaultVariants: {
224 | size: 'md',
225 | variant: 'line',
226 | },
227 | })
228 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/tag.recipe.tsx:
--------------------------------------------------------------------------------
1 | import { defineRecipe } from "@pandacss/dev";
2 | import { cssVar } from "@crepe-ui/shared";
3 | import { badgeRecipe, badgeVars } from "./badge.recipe";
4 |
5 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/tag.ts
6 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/components/src/tag/tag.tsx
7 | // didn't include the slots (container / closeButton / label) as it's not used in the app
8 |
9 | const vars = cssVar.scope("tag", [
10 | "bg",
11 | "color",
12 | "shadow",
13 | "min-height",
14 | "min-width",
15 | "font-size",
16 | "padding-inline",
17 | ]);
18 |
19 | export const tagRecipe = defineRecipe({
20 | className: "tag",
21 | base: {
22 | colorPalette: "gray",
23 | fontWeight: "medium",
24 | lineHeight: 1.2,
25 | outline: 0,
26 | [vars.color.name]: badgeVars.color.ref,
27 | [vars.bg.name]: badgeVars.bg.ref,
28 | [vars.shadow.name]: badgeVars.shadow.ref,
29 | color: vars.color.ref,
30 | bg: vars.bg.ref,
31 | boxShadow: vars.shadow.ref,
32 | borderRadius: "md",
33 | minH: vars["min-height"].ref,
34 | minW: vars["min-width"].ref,
35 | fontSize: vars["font-size"].ref,
36 | px: vars["padding-inline"].ref,
37 | _focusVisible: {
38 | [vars.shadow.name]: "shadows.outline",
39 | },
40 | //
41 | display: "inline-flex",
42 | verticalAlign: "top",
43 | alignItems: "center",
44 | maxWidth: "100%",
45 | },
46 | variants: {
47 | variant: badgeRecipe.variants?.variant ?? {},
48 | size: {
49 | sm: {
50 | [vars["min-height"].name]: "sizes.5",
51 | [vars["min-width"].name]: "sizes.5",
52 | [vars["font-size"].name]: "fontSizes.xs",
53 | [vars["padding-inline"].name]: "space.2",
54 | },
55 | md: {
56 | [vars["min-height"].name]: "sizes.6",
57 | [vars["min-width"].name]: "sizes.6",
58 | [vars["font-size"].name]: "fontSizes.sm",
59 | [vars["padding-inline"].name]: "space.2",
60 | },
61 | lg: {
62 | [vars["min-height"].name]: "sizes.8",
63 | [vars["min-width"].name]: "sizes.8",
64 | [vars["font-size"].name]: "fontSizes.md",
65 | [vars["padding-inline"].name]: "space.3",
66 | },
67 | },
68 | },
69 | defaultVariants: {
70 | size: "md",
71 | variant: "subtle",
72 | },
73 | });
74 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/textarea.recipe.ts:
--------------------------------------------------------------------------------
1 | import { defineRecipe } from '@pandacss/dev'
2 | import { inputRecipe } from './input.recipe'
3 |
4 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/textarea.ts
5 |
6 | export const textareaRecipe = defineRecipe({
7 | className: 'textarea',
8 | base: {
9 | ...inputRecipe.base?.field,
10 | paddingY: '2',
11 | minHeight: '20',
12 | lineHeight: 'short',
13 | verticalAlign: 'top',
14 | },
15 | variants: {
16 | variant: {
17 | outline: inputRecipe.variants?.variant?.outline.field ?? {},
18 | flushed: inputRecipe.variants?.variant?.flushed.field ?? {},
19 | filled: inputRecipe.variants?.variant?.filled.field ?? {},
20 | unstyled: inputRecipe.variants?.variant?.unstyled.field ?? {},
21 | },
22 | size: {
23 | xs: inputRecipe.variants?.size?.xs.field ?? {},
24 | sm: inputRecipe.variants?.size?.sm.field ?? {},
25 | md: inputRecipe.variants?.size?.md.field ?? {},
26 | lg: inputRecipe.variants?.size?.lg.field ?? {},
27 | },
28 | },
29 | defaultVariants: {
30 | size: 'md',
31 | variant: 'outline',
32 | },
33 | })
34 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/recipes/tooltip.recipe.ts:
--------------------------------------------------------------------------------
1 | import { tooltipAnatomy } from '@ark-ui/anatomy'
2 | import { cssVar } from '@crepe-ui/shared'
3 | import { defineSlotRecipe } from '@pandacss/dev'
4 | import { cssVariables } from '../vars'
5 |
6 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/tooltip.ts
7 |
8 | const vars = cssVariables.tooltip
9 | const arrow = cssVar.scope('arrow', ['size', 'size-half', 'background', 'offset'])
10 |
11 | export { vars as tooltipVars }
12 |
13 | export const tooltipRecipe = defineSlotRecipe({
14 | className: 'tooltip',
15 | slots: tooltipAnatomy.keys(),
16 | base: {
17 | positioner: {
18 | [vars['arrow-bg'].name]: vars.bg.ref,
19 | [vars.bg.name]: 'colors.gray.700',
20 | [vars.fg.name]: 'colors.whiteAlpha.900',
21 | _dark: {
22 | [vars.bg.name]: 'colors.gray.300',
23 | [vars.fg.name]: 'colors.gray.900',
24 | },
25 | },
26 | content: {
27 | bg: vars.bg.ref,
28 | color: vars.fg.ref,
29 | px: '2',
30 | py: '0.5',
31 | borderRadius: 'sm',
32 | fontWeight: 'medium',
33 | fontSize: 'sm',
34 | boxShadow: 'md',
35 | maxW: 'xs',
36 | zIndex: 'tooltip',
37 | _open: {
38 | animation: 'fade-in 0.25s ease-out',
39 | },
40 | _closed: {
41 | animation: 'fade-out 0.2s ease-out',
42 | },
43 | },
44 | arrow: {
45 | // this is from Ark-UI/ZagJS
46 | // https://github.com/chakra-ui/zag/blob/9a3a82f0b3738beda59c313fafd51360e6b0322f/website/data/components/tooltip.mdx#L261
47 | [arrow.size.name]: 'sizes.3',
48 | [arrow.background.name]: vars['arrow-bg'].ref,
49 | },
50 | arrowTip: {
51 | _open: {
52 | animation: 'fade-in 0.25s ease-out',
53 | },
54 | _closed: {
55 | animation: 'fade-out 0.2s ease-out',
56 | },
57 | },
58 | },
59 | })
60 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/semantic-tokens.ts:
--------------------------------------------------------------------------------
1 | import { defineSemanticTokens } from '@pandacss/dev'
2 |
3 | export const semanticTokens = defineSemanticTokens({
4 | colors: {
5 | // https://github.com/chakra-ui/chakra-ui/blob/v3/packages/theme/src/semantic-tokens.ts#L3
6 | 'chakra-body-text': {
7 | value: { base: '{colors.gray.800}', _dark: '{colors.whiteAlpha.900}' },
8 | },
9 | 'chakra-body-bg': { value: { base: 'white', _dark: '{colors.gray.800}' } },
10 | 'chakra-border-color': {
11 | value: { base: '{colors.gray.200}', _dark: '{colors.whiteAlpha.300}' },
12 | },
13 | 'chakra-inverse-text': {
14 | value: { base: 'white', _dark: '{colors.gray.800}' },
15 | },
16 | 'chakra-subtle-bg': {
17 | value: { base: '{colors.gray.100}', _dark: '{colors.gray.700}' },
18 | },
19 | 'chakra-subtle-text': {
20 | value: { base: '{colors.gray.600}', _dark: '{colors.gray.400}' },
21 | },
22 | 'chakra-placeholder-color': {
23 | value: { base: '{colors.gray.500}', _dark: '{colors.whiteAlpha.400}' },
24 | },
25 | background: {
26 | value: { base: '{colors.white}', _dark: '{colors.gray.800}' },
27 | },
28 | // https://github.com/chakra-ui/chakra-ui-docs/blob/71d44a6de8de3a221222f9b0cc2a4fc13916929e/theme.ts#L14
29 | foreground: {
30 | value: { base: '{colors.gray.700}', _dark: '{colors.gray.100}' },
31 | },
32 | // Used when implementing components that do not exist in Chakra UI but does in Ark-UI
33 | // made some slight tweaks to the colors (gray-palette -> gray)
34 | // https://github.com/cschroeter/park-ui/blob/1320a779d8ec2e6b6a0f9eeb9a9658aef4e2ea6f/packages/presets/src/theme/semantic-tokens.ts#L4
35 | bg: {
36 | canvas: {
37 | value: {
38 | base: '{colors.gray.25}',
39 | _dark: '{colors.gray.950}',
40 | },
41 | },
42 | default: {
43 | value: { base: '{colors.white}', _dark: '{colors.gray.800}' },
44 | },
45 | subtle: {
46 | value: {
47 | base: '{colors.gray.100}',
48 | _dark: '{colors.gray.900}',
49 | },
50 | },
51 | muted: {
52 | value: {
53 | base: '{colors.gray.200}',
54 | _dark: '{colors.gray.800}',
55 | },
56 | },
57 | emphasized: {
58 | value: {
59 | base: '{colors.gray.300}',
60 | _dark: '{colors.gray.600}',
61 | },
62 | },
63 | disabled: {
64 | value: {
65 | base: '{colors.gray.200}',
66 | _dark: '{colors.gray.700}',
67 | },
68 | },
69 | },
70 | fg: {
71 | default: {
72 | value: { base: '{colors.gray.950}', _dark: '{colors.white}' },
73 | },
74 | emphasized: {
75 | value: {
76 | base: '{colors.gray.700}',
77 | _dark: '{colors.gray.200}',
78 | },
79 | },
80 | muted: {
81 | value: {
82 | base: '{colors.gray.600}',
83 | _dark: '{colors.gray.300}',
84 | },
85 | },
86 | subtle: {
87 | value: {
88 | base: '{colors.gray.500}',
89 | _dark: '{colors.gray.400}',
90 | },
91 | },
92 | disabled: {
93 | value: {
94 | base: '{colors.gray.300}',
95 | _dark: '{colors.gray.700}',
96 | },
97 | },
98 | },
99 | accent: {
100 | default: {
101 | value: { base: '{colors.gray.950}', _dark: '{colors.white}' },
102 | },
103 | emphasized: {
104 | value: {
105 | base: '{colors.gray.800}',
106 | _dark: '{colors.gray.200}',
107 | },
108 | },
109 | fg: {
110 | value: { base: '{colors.white}', _dark: '{colors.gray.950}' },
111 | },
112 | },
113 |
114 | border: {
115 | default: {
116 | value: {
117 | base: '{colors.gray.200}',
118 | _dark: '{colors.gray.800}',
119 | },
120 | },
121 | emphasized: {
122 | value: {
123 | base: '{colors.gray.300}',
124 | _dark: '{colors.gray.700}',
125 | },
126 | },
127 | outline: {
128 | value: {
129 | base: '{colors.gray.600}',
130 | _dark: '{colors.gray.400}',
131 | },
132 | },
133 | accent: {
134 | value: { base: '{colors.gray.900}', _dark: '{colors.white}' },
135 | },
136 | disabled: {
137 | value: {
138 | base: '{colors.gray.200}',
139 | _dark: '{colors.gray.800}',
140 | },
141 | },
142 | },
143 | },
144 | shadows: {
145 | accent: {
146 | value: '0 0 0 1px {colors.border.accent}',
147 | },
148 | outline: {
149 | value: '0 0 0 1px {colors.border.outline}',
150 | },
151 | xs: {
152 | value: {
153 | base: '0px 1px 2px rgba(23, 23, 23, 0.1)',
154 | _dark: '0px 1px 2px rgba(0, 0, 0, 1.0)',
155 | },
156 | },
157 | sm: {
158 | value: {
159 | base: '0px 2px 4px rgba(23, 23, 23, 0.1)',
160 | _dark: '0px 2px 4px rgba(0, 0, 0, 1.0)',
161 | },
162 | },
163 | md: {
164 | value: {
165 | base: '0px 4px 8px rgba(23, 23, 23, 0.1)',
166 | _dark: '0px 4px 8px rgba(0, 0, 0, 1.0)',
167 | },
168 | },
169 | lg: {
170 | value: {
171 | base: '0px 8px 16px rgba(23, 23, 23, 0.1)',
172 | _dark: '0px 8px 16px rgba(0, 0, 0, 1.0)',
173 | },
174 | },
175 | xl: {
176 | value: {
177 | base: '0px 16px 24px rgba(23, 23, 23, 0.1)',
178 | _dark: '0px 16px 24px rgba(0, 0, 0, 1.0)',
179 | },
180 | },
181 | },
182 | })
183 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/text-styles.ts:
--------------------------------------------------------------------------------
1 | import { defineTextStyles } from '@pandacss/dev'
2 |
3 | // https://github.com/chakra-ui/chakra-ui/blob/f4b1ad66be1ada4b2728faef4c68a82a76f02532/packages/theme/src/components/heading.ts
4 |
5 | export const textStyles = defineTextStyles({
6 | heading: {
7 | '4xl': { value: { fontSize: { base: '6xl', md: '7xl' }, lineHeight: 1 } },
8 | '3xl': { value: { fontSize: { base: '5xl', md: '6xl' }, lineHeight: 1 } },
9 | '2xl': {
10 | value: {
11 | fontSize: { base: '4xl', md: '5xl' },
12 | lineHeight: { base: 1.2, md: 1 },
13 | },
14 | },
15 | xl: {
16 | value: {
17 | fontSize: { base: '3xl', md: '4xl' },
18 | lineHeight: { base: 1.33, md: 1.2 },
19 | },
20 | },
21 | lg: {
22 | value: {
23 | fontSize: { base: '2xl', md: '3xl' },
24 | lineHeight: { base: 1.33, md: 1.2 },
25 | },
26 | },
27 | md: { value: { fontSize: 'xl', lineHeight: 1.2 } },
28 | sm: { value: { fontSize: 'md', lineHeight: 1.2 } },
29 | xs: { value: { fontSize: 'sm', lineHeight: 1.2 } },
30 | },
31 | })
32 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/tokens.ts:
--------------------------------------------------------------------------------
1 | import { defineTokens } from '@pandacss/dev'
2 | import { themeFoundations } from './foundations'
3 | import { wrapValue } from '@crepe-ui/shared'
4 |
5 | export const tokens = defineTokens({
6 | borders: wrapValue(themeFoundations.borders),
7 | colors: wrapValue(themeFoundations.colors),
8 | fonts: wrapValue(themeFoundations.typography.fonts),
9 | fontSizes: wrapValue(themeFoundations.typography.fontSizes),
10 | fontWeights: wrapValue(themeFoundations.typography.fontWeights),
11 | letterSpacings: wrapValue(themeFoundations.typography.letterSpacings),
12 | lineHeights: wrapValue(themeFoundations.typography.lineHeights),
13 | shadows: wrapValue(themeFoundations.shadows),
14 | radii: wrapValue(themeFoundations.radii),
15 | sizes: wrapValue(themeFoundations.sizes),
16 | space: wrapValue(themeFoundations.space),
17 | zIndex: wrapValue(themeFoundations.zIndices),
18 | })
19 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/utilities.ts:
--------------------------------------------------------------------------------
1 | import { defineUtility } from '@pandacss/dev'
2 | import { colorMix } from '@crepe-ui/shared'
3 |
4 | const noOfLines = defineUtility({
5 | className: 'n-lines',
6 | values: { type: 'number' },
7 | transform(value) {
8 | return {
9 | overflow: 'hidden',
10 | textOverflow: 'ellipsis',
11 | display: '-webkit-box',
12 | WebkitBoxOrient: 'vertical',
13 | //@ts-ignore
14 | WebkitLineClamp: 'var(--chakra-line-clamp)',
15 | '--chakra-line-clamp': value,
16 | }
17 | },
18 | })
19 |
20 | const backgroundAlpha = defineUtility({
21 | shorthand: ['bga'],
22 | property: 'backgroundColor',
23 | className: 'bg-alpha',
24 | values: { type: 'string' },
25 | transform: (...args) => {
26 | const { value, color } = colorMix(...args)
27 |
28 | return {
29 | '--bga': value,
30 | backgroundColor: `var(--bga, ${color})`,
31 | }
32 | },
33 | })
34 |
35 | const colorAlpha = defineUtility({
36 | property: 'color',
37 | className: 'text-alpha',
38 | values: { type: 'string' },
39 | transform: (...args) => {
40 | const { value, color } = colorMix(...args)
41 |
42 | return {
43 | '--color-alpha': value,
44 | color: `var(--color-alpha, ${color})`,
45 | }
46 | },
47 | })
48 |
49 | export const utilities = {
50 | extend: {
51 | noOfLines,
52 | backgroundAlpha,
53 | colorAlpha,
54 | },
55 | }
56 |
--------------------------------------------------------------------------------
/packages/preset-chakra/src/vars.ts:
--------------------------------------------------------------------------------
1 | import { cssVar } from '@crepe-ui/shared'
2 |
3 | export const cssVariables = {
4 | badge: cssVar.scope('badge', [['bg', 'inherit'], ['bg-alpha', 'inherit'], ['color', 'inherit'], 'shadow']),
5 | skeleton: cssVar.scope('skeleton', ['start-color', 'end-color']),
6 | tooltip: cssVar.scope('tooltip', ['bg', 'fg', 'arrow-bg']),
7 | }
8 |
--------------------------------------------------------------------------------
/packages/preset-chakra/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "lib": ["ES2020", "DOM", "DOM.Iterable"]
5 | },
6 | "include": ["src"]
7 | }
8 |
--------------------------------------------------------------------------------
/packages/preset-chakra/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsup'
2 |
3 | export default defineConfig({
4 | entry: ['src/preset.ts', 'src/vars.ts'],
5 | clean: true,
6 | dts: true,
7 | format: ['esm', 'cjs'],
8 | onSuccess: 'cd ../components && pnpm static',
9 | })
10 |
--------------------------------------------------------------------------------
/packages/shared/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@crepe-ui/shared",
3 | "version": "0.0.1",
4 | "types": "./src/shared.ts",
5 | "main": "./dist/shared.js",
6 | "exports": {
7 | ".": {
8 | "source": "./src/shared.ts",
9 | "types": "./dist/shared.d.ts",
10 | "import": {
11 | "types": "./dist/shared.d.mts",
12 | "default": "./dist/shared.mjs"
13 | },
14 | "require": "./dist/shared.js"
15 | }
16 | },
17 | "scripts": {
18 | "dev": "tsup --watch",
19 | "build": "tsup"
20 | },
21 | "devDependencies": {
22 | "@pandacss/types": "^0.26.2",
23 | "tsup": "^7.2.0",
24 | "typescript": "^5.2.2"
25 | },
26 | "repository": {
27 | "type": "git",
28 | "url": "https://github.com/astahmer/crepe-ui",
29 | "directory": "packages/shared"
30 | },
31 | "sideEffects": false,
32 | "files": [
33 | "dist"
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/packages/shared/src/assign-inline-vars.ts:
--------------------------------------------------------------------------------
1 | import { TokenCategory } from '@pandacss/types'
2 | import { traverse } from './traverse'
3 | import { dashCase } from './utils'
4 |
5 | interface TokenRecord extends Record> {}
6 |
7 | export type ToTokenRecord> = {
8 | [Category in TokenCategory]-?: {
9 | [TokenName in TTokens[Category]]?: number | string
10 | }
11 | }
12 |
13 | // TODO handle hash
14 | export const assignInlineVars = (userVars?: Partial, prefix?: string) => {
15 | const vars = {} as TokenRecord
16 |
17 | for (const [category, tokens] of Object.entries(userVars ?? {})) {
18 | traverse(
19 | tokens,
20 | ({ value, path }) => {
21 | if (typeof value === 'string' || typeof value === 'number') {
22 | const cssVarRef = `--${prefix ? `${prefix}-` : ''}${dashCase(category)}-${dashCase(path.replace('.', '-'))}`
23 | vars[cssVarRef] = value as any
24 | }
25 | },
26 | { separator: '-' },
27 | )
28 | }
29 |
30 | return vars as Record
31 | }
32 |
--------------------------------------------------------------------------------
/packages/shared/src/color-mix.ts:
--------------------------------------------------------------------------------
1 | import type { PropertyTransform } from '@pandacss/types'
2 | import { CssVar, cssVar } from './css-var'
3 | import { dashCase } from './utils'
4 |
5 | export const colorMix: (...args: Parameters) => {
6 | color: string
7 | amount: string | number
8 | value: string
9 | } = (value: string, { token }) => {
10 | const [color, opacityAmount] = value.split('/')
11 | const amount = !isNaN(Number(opacityAmount)) ? Number(opacityAmount) : 100
12 | const colorValue = token(`colors.${color}`)
13 | const opacityValue = token(`opacity.${amount}`)
14 | const amountValue = opacityValue ? Number(opacityValue) * 100 : `${100 - amount}%`
15 |
16 | return {
17 | color: colorValue ?? color,
18 | amount: amountValue,
19 | value: `color-mix(in srgb, transparent ${amountValue}, ${colorValue})`,
20 | }
21 | }
22 |
23 | /**
24 | * @example tokenValue: `colorPalette.200` = `var(--colors-color-palette-200)`
25 | * @example tokenValue: `colorPalette.200/16` = `color-mix(in srgb, transparent var(--colors-color-palette-200)`
26 | */
27 | export const colorMixVar = (tokenValue: string, fallbackVar: CssVar) => {
28 | const [color, opacityAmount] = tokenValue.split('/')
29 | const amount = !isNaN(Number(opacityAmount)) ? Number(opacityAmount) : 100
30 | const amountValue = 100 - amount
31 |
32 | const colorName = dashCase(color).replace('.', '-')
33 | const colorVar = cssVar.create(`colors-${colorName}`)
34 |
35 | return {
36 | ref: colorVar.ref,
37 | colorMixValue: `color-mix(in srgb, transparent ${amountValue}%, ${colorVar.ref})` as const,
38 | value: `var(${fallbackVar?.name}, ${colorVar.ref})` as const,
39 | fallbackVar,
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/packages/shared/src/css-calc.ts:
--------------------------------------------------------------------------------
1 | import type { CssVar } from './css-var'
2 |
3 | function isObject(value: unknown): value is Record {
4 | const type = typeof value
5 | return value != null && (type === 'object' || type === 'function') && !Array.isArray(value)
6 | }
7 |
8 | export type Operand = string | number | CssVar
9 | type Operands = Operand[]
10 |
11 | type Operator = '+' | '-' | '*' | '/'
12 |
13 | function toRef(operand: Operand): string {
14 | if (isObject(operand) && operand.ref) {
15 | return operand.ref
16 | }
17 | return String(operand)
18 | }
19 |
20 | const toExpr = (operator: Operator, ...operands: Operands) =>
21 | operands.map(toRef).join(` ${operator} `).replace(/calc/g, '')
22 |
23 | const add = (...operands: Operands) => `calc(${toExpr('+', ...operands)})`
24 |
25 | const subtract = (...operands: Operands) => `calc(${toExpr('-', ...operands)})`
26 |
27 | const multiply = (...operands: Operands) => `calc(${toExpr('*', ...operands)})`
28 |
29 | const divide = (...operands: Operands) => `calc(${toExpr('/', ...operands)})`
30 |
31 | const negate = (x: Operand) => {
32 | const value = toRef(x)
33 |
34 | if (value != null && !Number.isNaN(parseFloat(value))) {
35 | return String(value).startsWith('-') ? String(value).slice(1) : `-${value}`
36 | }
37 |
38 | return multiply(value, -1)
39 | }
40 |
41 | export interface CalcChain {
42 | add: (...operands: Operands) => CalcChain
43 | subtract: (...operands: Operands) => CalcChain
44 | multiply: (...operands: Operands) => CalcChain
45 | divide: (...operands: Operands) => CalcChain
46 | negate: () => CalcChain
47 | toString: () => string
48 | }
49 |
50 | export const calc = Object.assign(
51 | (x: Operand): CalcChain => ({
52 | add: (...operands) => calc(add(x, ...operands)),
53 | subtract: (...operands) => calc(subtract(x, ...operands)),
54 | multiply: (...operands) => calc(multiply(x, ...operands)),
55 | divide: (...operands) => calc(divide(x, ...operands)),
56 | negate: () => calc(negate(x)),
57 | toString: () => x.toString(),
58 | }),
59 | {
60 | add,
61 | subtract,
62 | multiply,
63 | divide,
64 | negate,
65 | },
66 | )
67 |
--------------------------------------------------------------------------------
/packages/shared/src/css-var.ts:
--------------------------------------------------------------------------------
1 | const cssVarRef = (value: Value, fallback?: string) =>
2 | `var(--${value}${fallback ? ', ' + fallback : ''})` as const
3 | const cssVarName = (value: Value) => `--${value}` as const
4 | const createCssVar = (value: Value, fallback?: string) =>
5 | ({
6 | name: cssVarName(value),
7 | ref: cssVarRef(value, fallback),
8 | }) as CssVar
9 |
10 | export type CssVar = {
11 | name: `--${Name}`
12 | ref: `var(--${Name})`
13 | }
14 | export type ToCssVar = `--${Cat}-${T}`
15 |
16 | function defineCssVars(scope: string, keys: Array) {
17 | const vars = {} as Record>
18 | for (const key of keys) {
19 | if (Array.isArray(key)) {
20 | const [name, fallback] = key
21 | vars[name] = cssVar.create(`${scope}-${name}`, fallback) as CssVar
22 | continue
23 | }
24 |
25 | vars[key] = cssVar.create(`${scope}-${key}`) as CssVar
26 | }
27 | return vars as {
28 | [Var in K]: CssVar
29 | }
30 | }
31 |
32 | export const cssVar = {
33 | ref: cssVarRef,
34 | name: cssVarName,
35 | create: createCssVar,
36 | scope: defineCssVars,
37 | }
38 |
--------------------------------------------------------------------------------
/packages/shared/src/shared.ts:
--------------------------------------------------------------------------------
1 | export * from './assign-inline-vars'
2 | export * from './color-mix'
3 | export * from './css-calc'
4 | export * from './css-var'
5 | export * from './traverse'
6 | export * from './utils'
7 | export * from './wrap-value'
8 |
--------------------------------------------------------------------------------
/packages/shared/src/traverse.ts:
--------------------------------------------------------------------------------
1 | type CallbackFn = (args: CallbackItem) => void
2 | type CallbackItem = {
3 | value: any
4 | path: string
5 | depth: number
6 | parent: any[] | Record
7 | key: string
8 | }
9 |
10 | export const isObjectOrArray = (obj: unknown) => typeof obj === 'object' && obj !== null
11 | const defaultOptions = { separator: '.', maxDepth: Infinity }
12 |
13 | export function traverse(
14 | obj: any,
15 | callback: CallbackFn,
16 | options: { separator: string; maxDepth?: number } = defaultOptions,
17 | ): void {
18 | const maxDepth = options.maxDepth ?? defaultOptions.maxDepth
19 | const separator = options.separator ?? defaultOptions.separator
20 |
21 | const stack: CallbackItem[] = [{ value: obj, path: '', depth: -1, parent: null as any, key: '' }]
22 |
23 | while (stack.length > 0) {
24 | const currentItem = stack.pop()!
25 |
26 | // Call the callback function
27 | if (currentItem.parent !== null) {
28 | callback(currentItem)
29 | }
30 |
31 | // If the value is an object or array and depth is within limits, push its properties to the stack
32 | if (isObjectOrArray(currentItem.value) && currentItem.depth < maxDepth) {
33 | const keys = Object.keys(currentItem.value)
34 | for (let i = keys.length - 1; i >= 0; i--) {
35 | const key = keys[i]
36 | const value = currentItem.value[key]
37 | const path = currentItem.path ? currentItem.path + separator + key : key
38 | stack.push({
39 | value,
40 | path,
41 | depth: currentItem.depth + 1,
42 | parent: currentItem.value,
43 | key,
44 | })
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/packages/shared/src/utils.ts:
--------------------------------------------------------------------------------
1 | // https://github.com/chakra-ui/panda/blob/55eb7cce8a351bbe678edc15fb00d0d4b604576f/packages/shared/src/css-var.ts
2 | const dashCaseRegex = /[A-Z]/g
3 | export function dashCase(string: string) {
4 | return string.replace(dashCaseRegex, (match) => `-${match.toLowerCase()}`)
5 | }
6 |
7 | // https://github.com/chakra-ui/chakra-ui/blob/b904bbccab7d9c3b7ead48043b0e0652701f31f8/packages/utilities/src/common.ts
8 |
9 | export const ariaAttr = (condition: boolean | undefined) => (condition ? true : undefined)
10 |
11 | type Booleanish = boolean | 'true' | 'false'
12 | export const dataAttr = (condition: boolean | undefined) => (condition ? '' : undefined) as Booleanish
13 |
14 | type Args = T extends (...args: infer R) => any ? R : never
15 |
16 | export function callAllHandlers void>(...fns: (T | undefined)[]) {
17 | return function func(event: Args[0]) {
18 | fns.some((fn) => {
19 | fn?.(event)
20 | return event?.defaultPrevented
21 | })
22 | }
23 | }
24 |
25 | export function getInitials(name: string) {
26 | const names = name.split(' ')
27 | const firstName = names[0] ?? ''
28 | const lastName = names.length > 1 ? names[names.length - 1] : ''
29 | return firstName && lastName ? `${firstName.charAt(0)}${lastName.charAt(0)}` : firstName.charAt(0)
30 | }
31 |
--------------------------------------------------------------------------------
/packages/shared/src/wrap-value.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Recursively wraps each value in a { value: xxx } object
3 | */
4 | export const wrapValue = (obj: Record) => {
5 | const newObj: Record = {}
6 |
7 | for (const key in obj) {
8 | if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
9 | newObj[key] = wrapValue(obj[key]) // Recursive call for nested objects
10 | } else {
11 | newObj[key] = { value: obj[key] }
12 | }
13 | }
14 |
15 | return newObj
16 | }
17 |
--------------------------------------------------------------------------------
/packages/shared/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.build.json",
3 | "include": ["src"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/shared/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsup'
2 |
3 | export default defineConfig({
4 | entry: ['src/shared.ts'],
5 | clean: true,
6 | dts: true,
7 | format: ['esm', 'cjs'],
8 | })
9 |
--------------------------------------------------------------------------------
/packages/styled-system/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@crepe-ui/styled-system",
3 | "version": "0.0.0",
4 | "sideEffects": false,
5 | "type": "module",
6 | "exports": {
7 | "./css": {
8 | "types": "./dist/css/index.d.ts",
9 | "require": "./dist/css/index.mjs",
10 | "import": "./dist/css/index.mjs"
11 | },
12 | "./jsx": {
13 | "types": "./dist/jsx/index.d.ts",
14 | "require": "./dist/jsx/index.mjs",
15 | "import": "./dist/jsx/index.mjs"
16 | },
17 | "./patterns": {
18 | "types": "./dist/patterns/index.d.ts",
19 | "require": "./dist/patterns/index.mjs",
20 | "import": "./dist/patterns/index.mjs"
21 | },
22 | "./recipes": {
23 | "types": "./dist/recipes/index.d.ts",
24 | "require": "./dist/recipes/index.mjs",
25 | "import": "./dist/recipes/index.mjs"
26 | },
27 | "./tokens": {
28 | "types": "./dist/tokens/index.d.ts",
29 | "require": "./dist/tokens/index.mjs",
30 | "import": "./dist/tokens/index.mjs"
31 | },
32 | "./types": {
33 | "types": "./dist/types/index.d.ts"
34 | }
35 | },
36 | "scripts": {
37 | "build": "panda codegen",
38 | "prepare": "panda codegen"
39 | },
40 | "devDependencies": {
41 | "@crepe-ui/preset-chakra": "workspace:^",
42 | "@pandacss/dev": "^0.27.1",
43 | "@types/react": "^18.2.31",
44 | "@types/react-dom": "^18.2.14"
45 | },
46 | "repository": {
47 | "type": "git",
48 | "url": "https://github.com/astahmer/crepe-ui",
49 | "directory": "packages/styled-system"
50 | },
51 | "files": [
52 | "styled-system"
53 | ],
54 | "dependencies": {
55 | "@crepe-ui/shared": "workspace:^"
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/packages/styled-system/panda.config.ts:
--------------------------------------------------------------------------------
1 | import { defaultConfig } from '@crepe-ui/preset-chakra'
2 | import { defineConfig } from '@pandacss/dev'
3 |
4 | // This panda/config only exists to generate the lightweight `outdir` (styled-system) JS runtime
5 | // It's what transforms a style object into a className.
6 | // ex: `{ color: "red.300" }` => `text_red_300`
7 |
8 | export default defineConfig({
9 | presets: defaultConfig.presets,
10 |
11 | // Whether to use css reset
12 | preflight: false,
13 |
14 | outdir: 'dist',
15 | // using the same module specifier `@crepe-ui/styled-system` acrosss all packages
16 | // and by marking it as `external` in the `tsup.config.ts`
17 | // will make all packages use the same `styled-system` runtime
18 | importMap: '@crepe-ui/styled-system',
19 |
20 | // The JSX framework to use
21 | jsxFramework: 'react',
22 | })
23 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - "packages/**"
3 | - "storybook"
4 | - "sandbox/**"
5 |
--------------------------------------------------------------------------------
/storybook/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | ignorePatterns: ["*"],
4 | env: { browser: true, es2020: true },
5 | extends: [
6 | 'eslint:recommended',
7 | 'plugin:@typescript-eslint/recommended',
8 | 'plugin:react-hooks/recommended',
9 | ],
10 | ignorePatterns: ['dist', '.eslintrc.cjs'],
11 | parser: '@typescript-eslint/parser',
12 | plugins: ['react-refresh'],
13 | rules: {
14 | 'react-refresh/only-export-components': [
15 | 'warn',
16 | { allowConstantExport: true },
17 | ],
18 | },
19 | }
20 |
--------------------------------------------------------------------------------
/storybook/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/storybook/.storybook/main.ts:
--------------------------------------------------------------------------------
1 | import type { StorybookConfig } from '@storybook/react-vite'
2 |
3 | const config: StorybookConfig = {
4 | stories: ['../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
5 | addons: [
6 | '@storybook/addon-links',
7 | '@storybook/addon-essentials',
8 | '@storybook/addon-onboarding',
9 | '@storybook/addon-interactions',
10 | ],
11 | framework: {
12 | name: '@storybook/react-vite',
13 | options: {},
14 | },
15 | docs: {
16 | autodocs: 'tag',
17 | },
18 | }
19 | export default config
20 |
--------------------------------------------------------------------------------
/storybook/.storybook/preview.ts:
--------------------------------------------------------------------------------
1 | import type { Preview } from '@storybook/react'
2 | import '../stories/index.css'
3 | import '@crepe-ui/preset-chakra/reset.css'
4 | import '@crepe-ui/components/static.css'
5 |
6 | const preview: Preview = {
7 | parameters: {
8 | actions: { argTypesRegex: '^on[A-Z].*' },
9 | controls: {
10 | matchers: {
11 | color: /(background|color)$/i,
12 | date: /Date$/i,
13 | },
14 | },
15 | },
16 | }
17 |
18 | export default preview
19 |
--------------------------------------------------------------------------------
/storybook/README.md:
--------------------------------------------------------------------------------
1 | # React + TypeScript + Vite
2 |
3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4 |
5 | Currently, two official plugins are available:
6 |
7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9 |
10 | ## Expanding the ESLint configuration
11 |
12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
13 |
14 | - Configure the top-level `parserOptions` property like this:
15 |
16 | ```js
17 | export default {
18 | // other rules...
19 | parserOptions: {
20 | ecmaVersion: 'latest',
21 | sourceType: 'module',
22 | project: ['./tsconfig.json', './tsconfig.node.json'],
23 | tsconfigRootDir: __dirname,
24 | },
25 | }
26 | ```
27 |
28 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
29 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
30 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
31 |
--------------------------------------------------------------------------------
/storybook/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/storybook/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "storybook",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "prepare": "panda codegen",
8 | "dev": "storybook dev -p 6006",
9 | "build": "storybook build"
10 | },
11 | "dependencies": {
12 | "@crepe-ui/components": "workspace:^",
13 | "@crepe-ui/preset-chakra": "workspace:^",
14 | "@crepe-ui/shared": "workspace:^",
15 | "@crepe-ui/styled-system": "workspace:^",
16 | "react": "^18.2.0",
17 | "react-dom": "^18.2.0"
18 | },
19 | "devDependencies": {
20 | "@pandacss/dev": "^0.27.1",
21 | "@storybook/addon-essentials": "^7.6.4",
22 | "@storybook/addon-interactions": "^7.6.4",
23 | "@storybook/addon-links": "^7.6.4",
24 | "@storybook/addon-onboarding": "^1.0.9",
25 | "@storybook/blocks": "^7.6.4",
26 | "@storybook/react": "^7.6.4",
27 | "@storybook/react-vite": "^7.6.4",
28 | "@storybook/test": "^7.6.4",
29 | "@types/react": "^18.2.37",
30 | "@types/react-dom": "^18.2.15",
31 | "@typescript-eslint/eslint-plugin": "^6.10.0",
32 | "@typescript-eslint/parser": "^6.10.0",
33 | "@vitejs/plugin-react-swc": "^3.5.0",
34 | "eslint": "^8.53.0",
35 | "eslint-plugin-react-hooks": "^4.6.0",
36 | "eslint-plugin-react-refresh": "^0.4.4",
37 | "storybook": "^7.6.4",
38 | "typescript": "^5.2.2",
39 | "vite": "^5.0.0"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/storybook/panda.config.ts:
--------------------------------------------------------------------------------
1 | import { defaultConfig } from '@crepe-ui/preset-chakra'
2 | import { defineConfig } from '@pandacss/dev'
3 |
4 | export default defineConfig({
5 | presets: defaultConfig.presets,
6 | // staticCss: defaultConfig.staticCss,
7 | preflight: false,
8 | include: ['./stories/**/*.{js,jsx,ts,tsx}'],
9 | outdir: 'styled-system',
10 | importMap: '@crepe-ui/styled-system',
11 | jsxFramework: 'react',
12 |
13 | // This will override the @crepe-ui/styled-system token class generation
14 | theme: {
15 | extend: {
16 | tokens: {
17 | colors: {
18 | red: {
19 | 300: {
20 | value: 'yellow',
21 | },
22 | },
23 | },
24 | },
25 | },
26 | },
27 | })
28 |
--------------------------------------------------------------------------------
/storybook/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [require('@pandacss/dev/postcss')()]
3 | }
4 |
--------------------------------------------------------------------------------
/storybook/stories/Button.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@crepe-ui/components";
2 | import { Stack } from "@crepe-ui/styled-system/jsx";
3 | import { button } from "@crepe-ui/styled-system/recipes";
4 | import type { Meta, StoryObj } from "@storybook/react";
5 |
6 | const variantMapToStorybookSelect = (variantMap: Record) => {
7 | const controls = {} as Record;
8 | Object.entries(variantMap).forEach(([key, value]) => {
9 | controls[key] = { control: "select", options: value };
10 | });
11 |
12 | return controls;
13 | };
14 |
15 | const argTypes = variantMapToStorybookSelect(button.variantMap);
16 |
17 | const meta = {
18 | title: "Components/Button",
19 | component: Button,
20 | argTypes,
21 | args: {
22 | children: "Hello 🐼!",
23 | },
24 | tags: ["autodocs"],
25 | } satisfies Meta;
26 |
27 | export default meta;
28 | type Story = StoryObj;
29 |
30 | export const Default: Story = { args: {} };
31 |
32 | // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
33 | export const Variants: Story = {
34 | render: () => {
35 | return (
36 |
37 | {Object.entries(button.variantMap).map(([key, value]) => {
38 | return (
39 |
40 | {key}
41 |
42 | {value.map((variant) => {
43 | return (
44 |
47 | );
48 | })}
49 |
50 |
51 | );
52 | })}
53 |
54 | );
55 | },
56 | };
57 |
--------------------------------------------------------------------------------
/storybook/stories/OverridenToken.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from "@storybook/react";
2 | import { css } from "@crepe-ui/styled-system/css";
3 | import { PropsWithChildren } from "react";
4 |
5 | const ButtonWithOverridenToken = ({ children }: PropsWithChildren) => {
6 | return (
7 |
19 | );
20 | };
21 |
22 | const meta = {
23 | title: "Example/OverridenToken",
24 | component: ButtonWithOverridenToken,
25 | tags: ["autodocs"],
26 | decorators: [
27 | (Story) => (
28 |
29 |
30 |
31 | ),
32 | ],
33 | } satisfies Meta;
34 |
35 | export default meta;
36 | type Story = StoryObj;
37 |
38 | export const Default: Story = {
39 | args: {
40 | children: "Hello 🐼!",
41 | },
42 | };
43 |
--------------------------------------------------------------------------------
/storybook/stories/index.css:
--------------------------------------------------------------------------------
1 | @layer reset, base, tokens, recipes, utilities;
2 |
--------------------------------------------------------------------------------
/storybook/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "noEmit": true,
15 | "jsx": "react-jsx",
16 |
17 | /* Linting */
18 | "strict": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noFallthroughCasesInSwitch": true
22 | },
23 | "include": ["src", "stories"],
24 | "references": [{ "path": "./tsconfig.node.json" }]
25 | }
26 |
--------------------------------------------------------------------------------
/storybook/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "include": ["vite.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/storybook/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react-swc'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "strictNullChecks": true,
5 | "moduleResolution": "Bundler",
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 | "skipDefaultLibCheck": true,
9 | "jsx": "react-jsx",
10 | "incremental": true,
11 | "tsBuildInfoFile": ".tsbuildinfo",
12 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
13 | "verbatimModuleSyntax": false,
14 | "resolveJsonModule": true,
15 | "noEmit": true
16 | },
17 | "exclude": ["**/node_modules", "example-theme.ts", "old"]
18 | }
19 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.build.json",
3 | "compilerOptions": {
4 | "moduleResolution": "Bundler",
5 | "module": "ESNext",
6 | "customConditions": ["source"]
7 | },
8 | "exclude": ["**/node_modules", "example-theme.ts", "old"]
9 | }
10 |
--------------------------------------------------------------------------------