e.preventDefault()}
12 | {...args}
13 | />;
14 |
15 | export const Preview = Template.bind({});
16 | Preview.args = {
17 | children: 'Link content',
18 | underline: 'hover',
19 | href: '#yes'
20 | };
21 |
--------------------------------------------------------------------------------
/src/components/Anchor/index.js:
--------------------------------------------------------------------------------
1 | import { createElement } from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'classnames';
4 |
5 | export const Anchor = ({ as, underline, ...props }) => {
6 |
7 | let element = 'a';
8 | const anchorProps = {
9 | ...props,
10 | className: cx(
11 | 'text-rosewater',
12 | {
13 | 'decoration-lavender': underline != 'never',
14 | 'decoration-2': underline != 'never',
15 | 'hover:underline': underline == 'hover',
16 | 'underline': underline == 'always',
17 | }
18 | ),
19 | };
20 |
21 | if (as != null) {
22 | element = as;
23 | }
24 |
25 | return createElement(element, anchorProps, props.children);
26 |
27 | };
28 |
29 | Anchor.propTypes = {
30 | /**
31 | * Configure element to be used for rendering anchor tag
32 | */
33 | as: PropTypes.oneOfType([
34 | PropTypes.element,
35 | PropTypes.oneOf(['a']),
36 | ]),
37 | /**
38 | * Configure when underline should be shown
39 | */
40 | underline: PropTypes.oneOf([
41 | 'never',
42 | 'hover',
43 | 'always',
44 | ]),
45 | };
46 |
47 | Anchor.defaultProps = {
48 | underline: 'hover',
49 | as: 'a',
50 | };
--------------------------------------------------------------------------------
/src/components/Avatar/Avatar.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Avatar } from '.';
4 |
5 | export default {
6 | title: 'Components/Avatar',
7 | component: Avatar,
8 | };
9 |
10 | const Template = (args) => ;
11 |
12 | export const Name = Template.bind({});
13 | Name.args = {
14 | border: 'visible',
15 | background: 'mantle',
16 | name: 'Andreas Ekström',
17 | size: 'medium',
18 | };
19 |
20 | export const Image = Template.bind({});
21 | Image.args = {
22 | border: 'visible',
23 | src: 'https://aekstrom.me/assets/face.jpg',
24 | size: 'medium',
25 | };
26 |
--------------------------------------------------------------------------------
/src/components/Avatar/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import cx from 'classnames';
3 |
4 | export const AvatarBorderVariants = {
5 | none: 'border-0',
6 | visible: 'border-2 border-lavender',
7 | };
8 |
9 | export const AvatarBackgroundVariants = {
10 | crust: 'bg-crust',
11 | mantle: 'bg-mantle',
12 | };
13 |
14 | export const AvatarSizeVariations = {
15 | small: 'h-9 w-9 text-md',
16 | medium: 'h-12 w-12 text-xl',
17 | large: 'h-16 w-16 text-2xl',
18 | };
19 |
20 | export const Avatar = ({ src, name, ...props }) => {
21 |
22 | let initial = null;
23 | let last = null;
24 |
25 | if (name != null) {
26 | const split = name?.split(' ');
27 | initial = split[0]?.slice(0, 1);
28 |
29 | if (split.length > 1)
30 | last = split[split.length - 1]?.slice(0, 1);
31 | }
32 |
33 | return (
34 |
46 | {src != null ?
47 |
58 | : null}
59 |
60 | {src == null ?
61 |
62 | {initial?.toUpperCase()}
63 | {last?.toUpperCase()}
64 |
65 | : null}
66 |
67 | );
68 |
69 | };
70 |
71 | Avatar.defaultProps = {
72 | border: 'visible',
73 | background: 'mantle',
74 | size: 'medium',
75 | };
76 |
77 | Avatar.propTypes = {
78 | background: PropTypes.oneOf(Object.keys(AvatarBackgroundVariants)),
79 | border: PropTypes.oneOf(Object.keys(AvatarBorderVariants)),
80 | size: PropTypes.oneOf(Object.keys(AvatarSizeVariations)),
81 | };
--------------------------------------------------------------------------------
/src/components/Button/Button.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Button } from '.';
4 |
5 | // More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
6 | export default {
7 | title: 'Components/Button',
8 | component: Button,
9 | };
10 |
11 | // More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
12 | const Template = (args) => ;
13 |
14 | export const Preview = Template.bind({});
15 | // More on args: https://storybook.js.org/docs/react/writing-stories/args
16 | Preview.args = {
17 | primary: true,
18 | children: 'Button',
19 | };
20 |
--------------------------------------------------------------------------------
/src/components/Button/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import cx from 'classnames';
3 |
4 | export const Button = ({ primary, variant, ...props }) => {
5 |
6 | /** REQUIRE TAILWIND STYLES
7 | * bg-surface0
8 | * bg-surface1
9 | * bg-surface2
10 | * border-surface0
11 | * border-surface1
12 | * border-surface2
13 | * hover:bg-surface2
14 | * hover:border-surface2
15 | */
16 |
17 | const mode = primary ? 'bg' : 'border';
18 |
19 | return (
20 |
36 | );
37 | };
38 |
39 | Button.propTypes = {
40 | /**
41 | * Is this the principal call to action on the page?
42 | */
43 | primary: PropTypes.bool,
44 | /**
45 | * Controls color variant
46 | */
47 | variant: PropTypes.oneOf(['0', '1', '2']),
48 | /**
49 | * Optional click handler
50 | */
51 | onClick: PropTypes.func,
52 | };
53 |
54 | Button.defaultProps = {
55 | primary: true,
56 | variant: '0',
57 | onClick: undefined,
58 | };
59 |
--------------------------------------------------------------------------------
/src/components/Card/Card.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import {
4 | Card,
5 | CardBackgroundVariations,
6 | CardBorderVariations,
7 | } from '.';
8 |
9 | export default {
10 | title: 'Components/Card',
11 | component: Card,
12 | argTypes: {
13 | background: {
14 | control: {
15 | type: 'select',
16 | options: Object.keys(CardBackgroundVariations),
17 | }
18 | },
19 | border: {
20 | control: {
21 | type: 'select',
22 | options: Object.keys(CardBorderVariations),
23 | }
24 | },
25 | }
26 | };
27 |
28 | const Template = (args) => <>
29 |
30 |
31 | Card contents
32 |
33 |
34 | >;
35 |
36 | export const Preview = Template.bind({});
37 |
38 | Preview.args = {
39 | border: 'surface',
40 | background: 'crust',
41 | };
42 |
--------------------------------------------------------------------------------
/src/components/Card/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import cx from 'classnames';
3 | import { backgrounds, borders } from '../../constants/colors';
4 |
5 | export const CardBorderVariations = {
6 | ...borders,
7 | };
8 |
9 | export const CardBackgroundVariations = {
10 | ...backgrounds,
11 | };
12 |
13 | export const Card = ({ background, border, children }) => {
14 |
15 | return (
16 |
25 | {children}
26 |
27 | );
28 |
29 | };
30 |
31 | export const CardMeta = ({ children, className, ...rest }) => {
32 |
33 | return (
34 |
35 | {children}
36 |
37 | );
38 |
39 | };
40 |
41 | Card.defaultProps = {
42 | background: 'crust',
43 | border: 'surface',
44 | };
45 |
46 | Card.propTypes = {
47 | background: PropTypes.oneOf(Object.keys(CardBackgroundVariations)),
48 | border: PropTypes.oneOf(Object.keys(CardBorderVariations)),
49 | };
50 |
--------------------------------------------------------------------------------
/src/components/Checkbox/Checkbox.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Checkbox, CheckboxColorVariations } from '.';
4 |
5 | export default {
6 | title: 'Components/Checkbox',
7 | component: Checkbox,
8 | argTypes: {
9 | color: {
10 | control: {
11 | type: 'select',
12 | options: Object.keys(CheckboxColorVariations),
13 | }
14 | },
15 | }
16 | };
17 |
18 | const Template = (args) => ;
19 | const Multiple = (args) => <>
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | >;
28 |
29 | export const With_Label = Template.bind({});
30 |
31 | With_Label.args = {
32 | color: 'lavender',
33 | label: 'Label for checkbox'
34 | };
35 |
36 | export const Without_Label = Template.bind({});
37 |
38 | Without_Label.args = {
39 | color: 'lavender',
40 | label: null,
41 | };
42 |
43 | export const Disabled = Multiple.bind({});
44 |
45 | Disabled.args = {
46 | color: 'lavender',
47 | label: 'Label for disabled checkbox',
48 | disabled: true,
49 | };
50 |
--------------------------------------------------------------------------------
/src/components/Checkbox/_checkbox.css:
--------------------------------------------------------------------------------
1 | .checkbox-after::after {
2 | @apply transition-all;
3 | @apply delay-75;
4 | @apply duration-300;
5 | @apply block;
6 | @apply opacity-0;
7 | @apply w-3;
8 | @apply h-3;
9 | @apply bg-current;
10 |
11 | content: '';
12 | transform: scale(0.3);
13 | mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewbox='0 0 12 12' fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='2px'%3E%3Cpath d='M1.4,5.582,5.764,9.945,14.4,1.4' /%3E%3C/svg%3E");
14 | }
15 |
16 | .checkbox-after:checked::after {
17 | @apply opacity-100;
18 | @apply scale-100;
19 | }
--------------------------------------------------------------------------------
/src/components/Checkbox/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'classnames';
4 |
5 | import { useId } from '../../libs/id';
6 | import { typography } from '../../constants/colors';
7 |
8 | import './_checkbox.css';
9 |
10 | export const CheckboxColorVariations = {
11 | ...typography.base,
12 | ...typography.highlights,
13 | };
14 |
15 | export const Checkbox = ({ color, label, ...props }) => {
16 | const id = useId('cb');
17 |
18 | return (
19 | <>
20 |
46 |
47 | {label != null ?
48 |
55 | : null}
56 | >
57 | );
58 |
59 | };
60 |
61 | Checkbox.defaultProps = {
62 | color: 'lavender',
63 | label: null,
64 | disabled: false,
65 | };
66 |
67 | Checkbox.propTypes = {
68 | label: PropTypes.string,
69 | disabled: PropTypes.bool,
70 | color: PropTypes.oneOf(Object.keys(CheckboxColorVariations)),
71 | };
72 |
--------------------------------------------------------------------------------
/src/components/Heading/Heading.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Heading, HeadingColorVariations } from '.';
4 |
5 | export default {
6 | title: 'Components/Heading',
7 | component: Heading,
8 | argTypes: {
9 | color: {
10 | control: {
11 | type: 'select',
12 | options: Object.keys(HeadingColorVariations),
13 | }
14 | },
15 | }
16 | };
17 |
18 | const Template = (args) => ;
19 |
20 | export const Preview = Template.bind({});
21 | Preview.args = {
22 | size: '4xl',
23 | color: 'text',
24 | children: 'Heading 1',
25 | };
26 |
--------------------------------------------------------------------------------
/src/components/Heading/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import cx from 'classnames';
3 | import { typography } from '../../constants/colors';
4 |
5 | export const HeadingSizeVariations = {
6 | 'lg': 'text-lg',
7 | 'xl': 'text-xl',
8 | '2xl': 'text-2xl',
9 | '3xl': 'text-3xl',
10 | '4xl': 'text-4xl',
11 | '5xl': 'text-5xl',
12 | '6xl': 'text-6xl',
13 | '7xl': 'text-7xl',
14 | '8xl': 'text-8xl',
15 | '9xl': 'text-9xl',
16 | };
17 |
18 | export const HeadingColorVariations = {
19 | ...typography.base,
20 | ...typography.highlights
21 | };
22 |
23 | export const Heading = ({ className, size, color, ...props }) => {
24 |
25 | return (
26 |
34 | );
35 |
36 | };
37 |
38 | Heading.propTypes = {
39 | /**
40 | * Configure text size of Heading
41 | */
42 | size: PropTypes.oneOf(Object.keys(HeadingSizeVariations)),
43 |
44 | /**
45 | * Configure text color
46 | */
47 | color: PropTypes.oneOf(Object.keys(HeadingColorVariations)),
48 | };
49 |
50 | Heading.defaultProps = {
51 | size: 'xl',
52 | color: 'text',
53 | };
--------------------------------------------------------------------------------
/src/components/Input/Input.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Input } from '.';
4 |
5 | // More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
6 | export default {
7 | title: 'Components/Input',
8 | component: Input,
9 | };
10 |
11 | // More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
12 | const Template = (args) => ;
13 |
14 | export const Preview = Template.bind({});
15 | // More on args: https://storybook.js.org/docs/react/writing-stories/args
16 | Preview.args = {
17 | value: 'aekstr0m',
18 | placeholder: 'Insert username',
19 | };
20 |
--------------------------------------------------------------------------------
/src/components/Input/index.js:
--------------------------------------------------------------------------------
1 | import cx from 'classnames';
2 |
3 | export const Input = ({ className, ...props }) => {
4 |
5 | return (
6 |
22 | );
23 |
24 | }
25 |
26 | Input.defaultProps = {
27 | };
--------------------------------------------------------------------------------
/src/components/List/List.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { List } from '.';
4 |
5 | export default {
6 | title: 'Components/List',
7 | component: List,
8 | };
9 |
10 | const Template = (args) => (
11 |
12 | Item 1
13 | Item 2
14 | Item 3
15 | Item 4
16 |
17 | );
18 |
19 | export const Unordered = Template.bind({});
20 | Unordered.args = {
21 | type: 'unordered',
22 | listStyle: 'disc',
23 | color: 'foreground',
24 | markerColor: 'text',
25 | };
26 |
27 | export const Ordered = Template.bind({});
28 | Ordered.args = {
29 | type: 'ordered',
30 | listStyle: 'disc',
31 | color: 'foreground',
32 | markerColor: 'text',
33 | };
34 |
--------------------------------------------------------------------------------
/src/components/List/index.js:
--------------------------------------------------------------------------------
1 | import { createElement } from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'classnames';
4 |
5 | export const ListTypeVariations = {
6 | unordered: 'ul',
7 | ordered: 'ol',
8 | };
9 |
10 | export const ListStyleVariations = {
11 | none: 'list-none',
12 | disc: 'list-disc',
13 | decimal: 'list-decimal',
14 | };
15 |
16 | export const ListColorVariations = {
17 | foreground: 'text-text',
18 | subtext: 'text-subtext0',
19 | subtle: 'text-overlay1'
20 | };
21 |
22 | export const ListMarkerVariations = {
23 | text: 'marker:text-text',
24 | subtext: 'marker:text-subtext0',
25 | overlay: 'marker:text-overlay1',
26 | rosewater: 'marker:text-rosewater',
27 | green: 'marker:text-green',
28 | peach: 'marker:text-peach',
29 | maroon: 'marker:text-maroon',
30 | blue: 'marker:text-blue',
31 | red: 'marker:text-red',
32 | green: 'marker:text-green',
33 | yellow: 'marker:text-yellow',
34 | pink: 'marker:text-pink',
35 | teal: 'marker:text-teal',
36 | };
37 |
38 | export const List = ({ children, ...props }) => {
39 |
40 | const listProps = {
41 | className: cx(
42 | 'list-inside',
43 | ListColorVariations[props.color],
44 | ListStyleVariations[props.listStyle],
45 | ListMarkerVariations[props.markerColor],
46 | ),
47 | };
48 |
49 | return createElement(ListTypeVariations[props.type], listProps, children);
50 |
51 | };
52 |
53 | List.defaultProps = {
54 | type: 'unordered',
55 | listStyle: 'disc',
56 | color: 'foreground',
57 | markerColor: 'text',
58 | };
59 |
60 | List.propTypes = {
61 | color: PropTypes.oneOf(Object.keys(ListColorVariations)),
62 | type: PropTypes.oneOf(Object.keys(ListTypeVariations)),
63 | listStyle: PropTypes.oneOf(Object.keys(ListStyleVariations)),
64 | markerColor: PropTypes.oneOf(Object.keys(ListMarkerVariations)),
65 | };
66 |
--------------------------------------------------------------------------------
/src/components/Paragraph/Paragraph.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Paragraph, ParagraphColorVariations } from '.';
4 |
5 | export default {
6 | title: 'Components/Paragraph',
7 | component: Paragraph,
8 | argTypes: {
9 | color: {
10 | control: {
11 | type: 'select',
12 | options: Object.keys(ParagraphColorVariations),
13 | }
14 | },
15 | }
16 | };
17 |
18 | const Template = (args) => ;
19 |
20 | export const Foreground = Template.bind({});
21 | Foreground.args = {
22 | children: 'Text in paragraph',
23 | color: 'text',
24 | size: 'base',
25 | };
26 |
27 | export const Subtext = Template.bind({});
28 | Subtext.args = {
29 | children: 'Text in paragraph',
30 | color: 'subtext',
31 | size: 'base',
32 | };
33 |
34 | export const Overlay = Template.bind({});
35 | Overlay.args = {
36 | children: 'Text in paragraph',
37 | color: 'overlay',
38 | size: 'base',
39 | };
40 |
--------------------------------------------------------------------------------
/src/components/Paragraph/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import cx from 'classnames';
3 | import { typography } from '../../constants/colors';
4 |
5 | export const ParagraphSizeVariations = {
6 | xs: 'text-xs',
7 | sm: 'text-sm',
8 | base: 'text-[1rem]',
9 | lg: 'text-lg',
10 | xl: 'text-xl',
11 | '2xl': 'text-2xl',
12 | '3xl': 'text-3xl',
13 | '4xl': 'text-4xl',
14 | '5xl': 'text-5xl',
15 | '6xl': 'text-6xl',
16 | '7xl': 'text-7xl',
17 | '8xl': 'text-8xl',
18 | '9xl': 'text-9xl',
19 | };
20 |
21 | export const ParagraphColorVariations = {
22 | ...typography.base,
23 | };
24 |
25 | export const Paragraph = ({ children, ...props }) => {
26 |
27 | return (
28 |
34 | {children}
35 |
36 | );
37 |
38 | };
39 |
40 | Paragraph.defaultProps = {
41 | size: 'text',
42 | color: 'foreground',
43 | };
44 |
45 | Paragraph.propTypes = {
46 | color: PropTypes.oneOf(Object.keys(ParagraphColorVariations)),
47 | size: PropTypes.oneOf(Object.keys(ParagraphSizeVariations)),
48 | };
49 |
--------------------------------------------------------------------------------
/src/components/Radio/Radio.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Radio, RadioColorVariations } from '.';
4 |
5 | export default {
6 | title: 'Components/Radio',
7 | component: Radio,
8 | argTypes: {
9 | color: {
10 | control: {
11 | type: 'select',
12 | options: Object.keys(RadioColorVariations),
13 | }
14 | },
15 | }
16 | };
17 |
18 | const Template = (args) => <>
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | >;
27 |
28 | export const With_Label = Template.bind({});
29 |
30 | With_Label.args = {
31 | color: 'lavender',
32 | label: 'Label for radio'
33 | };
34 |
35 | export const Without_Label = Template.bind({});
36 |
37 | Without_Label.args = {
38 | color: 'lavender',
39 | label: null,
40 | };
41 |
42 | export const Disabled = Template.bind({});
43 |
44 | Disabled.args = {
45 | color: 'lavender',
46 | label: 'Label for disabled radio',
47 | disabled: true,
48 | };
49 |
--------------------------------------------------------------------------------
/src/components/Radio/_radio.css:
--------------------------------------------------------------------------------
1 | .radio-after::after {
2 | @apply transition-all;
3 | @apply delay-75;
4 | @apply duration-300;
5 | @apply block;
6 | @apply opacity-0;
7 | @apply w-2;
8 | @apply h-2;
9 | @apply bg-current;
10 | @apply rounded-full;
11 |
12 | content: '';
13 | transform: scale(0.3);
14 | }
15 |
16 | .radio-after:checked::after {
17 | @apply opacity-100;
18 | @apply scale-100;
19 | }
--------------------------------------------------------------------------------
/src/components/Radio/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import cx from 'classnames';
3 |
4 | import { useId } from '../../libs/id';
5 | import { typography } from '../../constants/colors';
6 |
7 | import './_radio.css';
8 |
9 | export const RadioColorVariations = {
10 | ...typography.base,
11 | ...typography.highlights,
12 | };
13 |
14 | export const Radio = ({ color, label, ...props }) => {
15 | const id = useId('rd');
16 |
17 | return (
18 |
19 |
45 |
46 | {label != null ?
47 |
54 | : null}
55 |
56 | );
57 |
58 | };
59 |
60 | Radio.defaultProps = {
61 | color: 'lavender',
62 | label: null,
63 | disabled: false,
64 | };
65 |
66 | Radio.propTypes = {
67 | label: PropTypes.string,
68 | disabled: PropTypes.bool,
69 | color: PropTypes.oneOf(Object.keys(RadioColorVariations)),
70 | };
71 |
--------------------------------------------------------------------------------
/src/components/Switch/Switch.stories.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import { Switch, SwitchColorVariations } from '.';
4 |
5 | export default {
6 | title: 'Components/Switch',
7 | component: Switch,
8 | argTypes: {
9 | color: {
10 | control: {
11 | type: 'select',
12 | options: Object.keys(SwitchColorVariations),
13 | }
14 | },
15 | }
16 | };
17 |
18 | const Template = (args) => {
19 | const [one, setOne] = useState(true);
20 | const [two, setTwo] = useState(false);
21 |
22 | return
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
;
31 | };
32 |
33 | export const Preview = Template.bind({});
34 | Preview.args = {
35 | color: 'lavender',
36 | label: 'Switch label',
37 | };
38 |
--------------------------------------------------------------------------------
/src/components/Switch/_switch.css:
--------------------------------------------------------------------------------
1 | .switch-pseudo::before, .switch-pseudo::after {
2 | @apply absolute;
3 | @apply transition-all;
4 | @apply duration-300;
5 | @apply block;
6 | @apply w-3;
7 | @apply h-3;
8 | @apply bg-surface2;
9 |
10 | content: '';
11 | }
12 |
13 | .switch-pseudo::before {
14 | @apply opacity-0;
15 | left: .3rem;
16 | mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewbox='0 0 12 12' fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='1px'%3E%3Cpath d='M1.4,5.582,5.764,9.945,14.4,1.4' /%3E%3C/svg%3E");
17 | transform: translateX(-1ch);
18 | }
19 |
20 | .switch-pseudo.active::before {
21 | @apply bg-current;
22 | @apply opacity-100;
23 | transform: translateX(0);
24 | }
25 |
26 | .switch-pseudo::after {
27 | @apply opacity-100;
28 | right: .3rem;
29 | mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg' fill='currentColor'%3E%3Cpath d='M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z'/%3E%3C/svg%3E");
30 | transform: translateX(0);
31 | }
32 |
33 | .switch-pseudo.active::after {
34 | @apply opacity-0;
35 | transform: translateX(1ch);
36 | }
--------------------------------------------------------------------------------
/src/components/Switch/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'classnames';
4 |
5 | import { useId } from '../../libs/id';
6 | import { typography } from '../../constants/colors';
7 |
8 | import './_switch.css';
9 |
10 | export const SwitchColorVariations = {
11 | ...typography.base,
12 | ...typography.highlights,
13 | };
14 |
15 | export const Switch = ({ color, value, label, ...props }) => {
16 | const id = useId('sw');
17 |
18 | /**
19 | * @param {Event} event
20 | */
21 | const onClickEvent = (event) => {
22 | event.preventDefault();
23 |
24 | if (props.onChange != null && typeof props.onChange === 'function') {
25 | props.onChange(!value);
26 | }
27 | };
28 |
29 | /**
30 | * @param {KeyboardEvent} event
31 | */
32 | const onKeyEvent = (event) => {
33 | if (event.code !== 'Space' && event.code !== 'Enter') {
34 | return false;
35 | }
36 |
37 | event.preventDefault();
38 |
39 | if (props.onChange != null && typeof props.onChange === 'function') {
40 | props.onChange(!value);
41 | }
42 | }
43 |
44 | return (
45 |
55 |
79 |
94 |
95 |
96 | {label != null ?
97 |
98 | {label}
99 |
100 | : null}
101 |
102 | );
103 |
104 | };
105 |
106 | Switch.defaultProps = {
107 | color: 'lavender',
108 | value: false,
109 | label: null,
110 | onChange: () => {},
111 | };
112 |
113 | Switch.propTypes = {
114 | color: PropTypes.oneOf(Object.keys(SwitchColorVariations)),
115 | value: PropTypes.bool,
116 | label: PropTypes.string,
117 | onChange: PropTypes.func,
118 | };
119 |
--------------------------------------------------------------------------------
/src/constants/colors.js:
--------------------------------------------------------------------------------
1 | export const backgrounds = {
2 | base: 'bg-base',
3 | crust: 'bg-crust',
4 | mantle: 'bg-mantle',
5 | 'surface0': 'bg-surface0',
6 | 'surface1': 'bg-surface1',
7 | 'overlay0': 'bg-overlay0',
8 | 'overlay1': 'bg-overlay1',
9 | 'overlay2': 'bg-overlay2',
10 | };
11 |
12 | export const borders = {
13 | base: 'border-base',
14 | crust: 'border-crust',
15 | mantle: 'border-mantle',
16 | surface: 'border-surface1',
17 | overlay: 'border-overlay2',
18 | };
19 |
20 | export const typography = {
21 | base: {
22 | text: 'text-text',
23 | subtext: 'text-subtext0',
24 | overlay: 'text-overlay1',
25 | },
26 | highlights: {
27 | rosewater: 'text-rosewater',
28 | green: 'text-green',
29 | peach: 'text-peach',
30 | maroon: 'text-maroon',
31 | blue: 'text-blue',
32 | red: 'text-red',
33 | green: 'text-green',
34 | yellow: 'text-yellow',
35 | pink: 'text-pink',
36 | teal: 'text-teal',
37 | lavender: 'text-lavender',
38 | },
39 | };
40 |
--------------------------------------------------------------------------------
/src/fonts.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;800;900&display=swap');
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import './fonts.css';
2 | import './main.css';
3 |
4 | export const palettes = [
5 | 'latte',
6 | 'frappe',
7 | 'macchiato',
8 | 'mocha',
9 | ];
10 |
11 | export * from './components/Anchor';
12 | export * from './components/Avatar';
13 | export * from './components/Button';
14 | export * from './components/Card';
15 | export * from './components/Checkbox';
16 | export * from './components/Heading';
17 | export * from './components/Input';
18 | export * from './components/List';
19 | export * from './components/Paragraph';
20 | export * from './components/Radio';
21 | export * from './components/Switch';
22 |
--------------------------------------------------------------------------------
/src/libs/id.js:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 | import { unique } from "./shorthash";
3 |
4 | /**
5 | * @param {string} prefix
6 | */
7 | export const useId = (prefix = null) => {
8 | return useMemo(() => {
9 | const hash = unique((Math.random().toString(36)+'00000000000000000').slice(2, 7));
10 |
11 | return prefix != null ?
12 | `${prefix}-${hash}`
13 | : hash;
14 | }, []);
15 | };
16 |
--------------------------------------------------------------------------------
/src/libs/shorthash.js:
--------------------------------------------------------------------------------
1 | /*
2 | shorthash
3 | (c) 2013 Bibig
4 |
5 | https://github.com/bibig/node-shorthash
6 | shorthash may be freely distributed under the MIT license.
7 | */
8 | export function bitwise(str) {
9 | var hash = 0;
10 | if (str.length == 0) return hash;
11 | for (var i = 0; i < str.length; i++) {
12 | var ch = str.charCodeAt(i);
13 | hash = ((hash << 5) - hash) + ch;
14 | hash = hash & hash; // Convert to 32bit integer
15 | }
16 | return hash;
17 | }
18 |
19 | export function binaryTransfer(integer, binary) {
20 | binary = binary || 62;
21 | var stack = [];
22 | var num;
23 | var result = '';
24 | var sign = integer < 0 ? '-' : '';
25 |
26 | function table(num) {
27 | var t = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
28 | return t[num];
29 | }
30 |
31 | integer = Math.abs(integer);
32 |
33 | while (integer >= binary) {
34 | num = integer % binary;
35 | integer = Math.floor(integer / binary);
36 | stack.push(table(num));
37 | }
38 |
39 | if (integer > 0) {
40 | stack.push(table(integer));
41 | }
42 |
43 | for (var i = stack.length - 1; i >= 0; i--) {
44 | result += stack[i];
45 | }
46 |
47 | return sign + result;
48 | }
49 |
50 | export function unique(text) {
51 | var id = binaryTransfer(bitwise(text), 61);
52 | return id.replace('-', 'Z');
53 | }
54 |
--------------------------------------------------------------------------------
/src/libs/variants.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * @param {string} type Appendend before -value. e.g. text or bg
4 | * @param {Array} constants List of items
5 | * @returns {object}
6 | */
7 | export const buildVariantFromConstant = (type, constants) => {
8 | return constants.reduce((current, element) => {
9 | current[element] = `${type}-${element}`;
10 | return current;
11 | }, {})
12 | };
13 |
--------------------------------------------------------------------------------
/src/main.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | body {
6 | @apply font-inter bg-base text-text;
7 | }
--------------------------------------------------------------------------------
/src/patterns/Cards.stories.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | Avatar,
3 | Card,
4 | CardMeta,
5 | Heading,
6 | } from '../index';
7 |
8 | export default {
9 | title: 'Patterns/Cards',
10 | };
11 |
12 | const PostCard = (args) => {
13 |
14 | return (
15 |
16 |
17 |
18 | 22 october 2022 - 6:00
19 |
20 |
21 |
22 | Main content
23 |
24 |
25 | A nice card description
26 |
27 |
28 | Andreas Ekström
29 |
30 |
31 |
32 | );
33 |
34 | };
35 |
36 | export const Post = PostCard.bind({});
--------------------------------------------------------------------------------
/src/patterns/Forms.stories.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | Anchor,
3 | Button,
4 | Checkbox,
5 | Input,
6 | Heading,
7 | } from '../index';
8 |
9 | export default {
10 | title: 'Patterns/Forms',
11 | };
12 |
13 | const SignupForm = () => {
14 |
15 | return (
16 |
17 |
18 | Welcome new user!
19 |
20 |
21 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | Sign up
41 |
42 |
43 |
44 | Already have an account?
Sign in
45 |
46 |
47 |
48 | );
49 |
50 | };
51 |
52 | const SigninForm = () => {
53 |
54 | return (
55 |
56 |
57 | Welcome back!
58 |
59 |
60 |
67 |
68 |
69 |
70 | Sign in
71 |
72 |
73 |
74 | Don't have an account?
Sign up
75 |
76 |
77 |
78 | );
79 |
80 | };
81 |
82 | export const Sign_up = SignupForm.bind({});
83 | export const Sign_in = SigninForm.bind({});
--------------------------------------------------------------------------------
/src/stories/Introduction.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/addon-docs';
2 | import Code from './assets/code-brackets.svg';
3 | import Colors from './assets/colors.svg';
4 | import Comments from './assets/comments.svg';
5 | import Direction from './assets/direction.svg';
6 | import Flow from './assets/flow.svg';
7 | import Plugin from './assets/plugin.svg';
8 | import Repo from './assets/repo.svg';
9 | import StackAlt from './assets/stackalt.svg';
10 |
11 |
12 |
13 |
116 |
117 | # Welcome to Storybook
118 |
119 | Storybook helps you build UI components in isolation from your app's business logic, data, and context.
120 | That makes it easy to develop hard-to-reach states. Save these UI states as **stories** to revisit during development, testing, or QA.
121 |
122 | Browse example stories now by navigating to them in the sidebar.
123 | View their code in the `stories` directory to learn how they work.
124 | We recommend building UIs with a [**component-driven**](https://componentdriven.org) process starting with atomic components and ending with pages.
125 |
126 | Configure
127 |
128 |
174 |
175 | Learn
176 |
177 |
207 |
208 |
209 | Tip Edit the Markdown in{' '}
210 | stories/Introduction.stories.mdx
211 |
212 |
--------------------------------------------------------------------------------
/src/stories/assets/code-brackets.svg:
--------------------------------------------------------------------------------
1 | illustration/code-brackets
--------------------------------------------------------------------------------
/src/stories/assets/colors.svg:
--------------------------------------------------------------------------------
1 | illustration/colors
--------------------------------------------------------------------------------
/src/stories/assets/comments.svg:
--------------------------------------------------------------------------------
1 | illustration/comments
--------------------------------------------------------------------------------
/src/stories/assets/direction.svg:
--------------------------------------------------------------------------------
1 | illustration/direction
--------------------------------------------------------------------------------
/src/stories/assets/flow.svg:
--------------------------------------------------------------------------------
1 | illustration/flow
--------------------------------------------------------------------------------
/src/stories/assets/plugin.svg:
--------------------------------------------------------------------------------
1 | illustration/plugin
--------------------------------------------------------------------------------
/src/stories/assets/repo.svg:
--------------------------------------------------------------------------------
1 | illustration/repo
--------------------------------------------------------------------------------
/src/stories/assets/stackalt.svg:
--------------------------------------------------------------------------------
1 | illustration/stackalt
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | './src/**/*.{html,js,jsx}',
5 | './node_modules/@catppuccin/ui/**/*.{html,js,jsx}',
6 | ],
7 | theme: {
8 | extend: {
9 | fontFamily: {
10 | 'inter': ["Inter", "ui-sans-serif"],
11 | },
12 | },
13 | },
14 | plugins: [
15 | require('@catppuccin/tailwindcss')({
16 | prefix: false,
17 | defaultFlavour: "macchiato",
18 | }),
19 | ],
20 | };
21 |
--------------------------------------------------------------------------------