refactor: Refactor UI structure
This commit is contained in:
33 files changed
+435
-247
No files matched your search
@@ -55,6 +55,7 @@
|
|||||||
"@storybook/react-vite": "^8.6.15",
|
"@storybook/react-vite": "^8.6.15",
|
||||||
"@storybook/test": "^8.6.15",
|
"@storybook/test": "^8.6.15",
|
||||||
"@storybook/test-runner": "^0.19.1",
|
"@storybook/test-runner": "^0.19.1",
|
||||||
|
"@tailwindcss/typography": "^0.5.19",
|
||||||
"@tailwindcss/vite": "^4.1.4",
|
"@tailwindcss/vite": "^4.1.4",
|
||||||
"@testing-library/jest-dom": "^6.9.1",
|
"@testing-library/jest-dom": "^6.9.1",
|
||||||
"@testing-library/react": "^16.3.1",
|
"@testing-library/react": "^16.3.1",
|
||||||
|
|||||||
+5
-5
@@ -1,11 +1,11 @@
|
|||||||
import { PaperArticle } from './PaperArticle';
|
import { Article } from './Article';
|
||||||
|
|
||||||
|
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
const meta: Meta<typeof PaperArticle> = {
|
const meta: Meta<typeof Article> = {
|
||||||
title: 'Components/Paper/PaperArticle',
|
title: 'components/Article',
|
||||||
component: PaperArticle,
|
component: Article,
|
||||||
tags: ['autodocs'],
|
tags: ['autodocs'],
|
||||||
argTypes: {
|
argTypes: {
|
||||||
onPress: { action: 'pressed' },
|
onPress: { action: 'pressed' },
|
||||||
@@ -16,7 +16,7 @@ export default meta;
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
type Story = StoryObj<typeof PaperArticle>;
|
type Story = StoryObj<typeof Article>;
|
||||||
|
|
||||||
export const Default: Story = {
|
export const Default: Story = {
|
||||||
args: {
|
args: {
|
||||||
+6
-6
@@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { PaperArticle } from './PaperArticle';
|
import { Article } from './Article';
|
||||||
import { render, screen } from '../../../test-utils';
|
import { render, screen } from '../../test-utils';
|
||||||
|
|
||||||
describe('components/paper/PaperArticle', () => {
|
describe('components/paper/Article', () => {
|
||||||
const props = {
|
const props = {
|
||||||
headline: 'Breaking News',
|
headline: 'Breaking News',
|
||||||
subHeadline: 'Local cat saves city',
|
subHeadline: 'Local cat saves city',
|
||||||
@@ -11,7 +11,7 @@ describe('components/paper/PaperArticle', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
it('renders all parts of the article', () => {
|
it('renders all parts of the article', () => {
|
||||||
render(<PaperArticle {...props}>Article body content</PaperArticle>);
|
render(<Article {...props}>Article body content</Article>);
|
||||||
|
|
||||||
expect(screen.getByText('Breaking News')).toBeInTheDocument();
|
expect(screen.getByText('Breaking News')).toBeInTheDocument();
|
||||||
expect(screen.getByText('Local cat saves city')).toBeInTheDocument();
|
expect(screen.getByText('Local cat saves city')).toBeInTheDocument();
|
||||||
@@ -20,11 +20,11 @@ describe('components/paper/PaperArticle', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('renders separators only when appropriate', () => {
|
it('renders separators only when appropriate', () => {
|
||||||
const { rerender } = render(<PaperArticle headline="Title">Body</PaperArticle>);
|
const { rerender } = render(<Article headline="Title">Body</Article>);
|
||||||
// Should have 1 separator (between title and body)
|
// Should have 1 separator (between title and body)
|
||||||
expect(screen.getAllByRole('separator')).toHaveLength(1);
|
expect(screen.getAllByRole('separator')).toHaveLength(1);
|
||||||
|
|
||||||
rerender(<PaperArticle headline="Title" subHeadline="Sub">Body</PaperArticle>);
|
rerender(<Article headline="Title" subHeadline="Sub">Body</Article>);
|
||||||
// Should have 2 separators (Title-Sub and Sub-Body)
|
// Should have 2 separators (Title-Sub and Sub-Body)
|
||||||
expect(screen.getAllByRole('separator')).toHaveLength(2);
|
expect(screen.getAllByRole('separator')).toHaveLength(2);
|
||||||
});
|
});
|
||||||
+14
-14
@@ -1,17 +1,17 @@
|
|||||||
import React, { memo } from 'react';
|
import React, { memo } from 'react';
|
||||||
|
|
||||||
import { Container } from '../../generic/Container/Container';
|
import { Container } from '../Container/Container';
|
||||||
import { Header } from '../../generic/Header/Header';
|
import { Header } from '../Header/Header';
|
||||||
import { Text } from '../../generic/Text/Text';
|
import { Separator } from '../Separator/Separator';
|
||||||
import { VStack } from '../../generic/VStack/VStack';
|
import { Text } from '../Text/Text';
|
||||||
import { PaperSeparator } from '../PaperSeparator/PaperSeparator';
|
import { VStack } from '../VStack/VStack';
|
||||||
|
|
||||||
import type { ContainerProps } from '../../generic/Container/Container';
|
import type { ContainerProps } from '../Container/Container';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Props for the newspaper-style PaperArticle component.
|
* Props for the Article component.
|
||||||
*/
|
*/
|
||||||
export interface PaperArticleProps {
|
export interface ArticleProps {
|
||||||
/** The main article headline */
|
/** The main article headline */
|
||||||
headline: string;
|
headline: string;
|
||||||
/** Optional sub-headline displayed between separators */
|
/** Optional sub-headline displayed between separators */
|
||||||
@@ -25,7 +25,7 @@ export interface PaperArticleProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A newspaper-style article component with a structured layout.
|
* A article component with a structured layout.
|
||||||
*
|
*
|
||||||
* Features:
|
* Features:
|
||||||
* - Centered medium headline (headline-m)
|
* - Centered medium headline (headline-m)
|
||||||
@@ -35,13 +35,13 @@ export interface PaperArticleProps {
|
|||||||
* - Optional bottom gray italic label (label-s)
|
* - Optional bottom gray italic label (label-s)
|
||||||
* - Interactive wrapper via Container
|
* - Interactive wrapper via Container
|
||||||
*/
|
*/
|
||||||
export const PaperArticle = memo(function PaperArticle({
|
export const Article = memo(function Article({
|
||||||
headline,
|
headline,
|
||||||
subHeadline,
|
subHeadline,
|
||||||
additionalText,
|
additionalText,
|
||||||
onPress,
|
onPress,
|
||||||
children,
|
children,
|
||||||
}: PaperArticleProps) {
|
}: ArticleProps) {
|
||||||
return (
|
return (
|
||||||
<Container padding={4} onPress={onPress}>
|
<Container padding={4} onPress={onPress}>
|
||||||
<VStack align="stretch" gap={4}>
|
<VStack align="stretch" gap={4}>
|
||||||
@@ -52,13 +52,13 @@ export const PaperArticle = memo(function PaperArticle({
|
|||||||
<VStack align="center" gap={2}>
|
<VStack align="center" gap={2}>
|
||||||
{subHeadline ? (
|
{subHeadline ? (
|
||||||
<>
|
<>
|
||||||
<PaperSeparator className="w-3/4" type="single" />
|
<Separator className="w-3/4" type="single" />
|
||||||
<Text italic className="text-center" size="m" type="label">
|
<Text italic className="text-center" size="m" type="label">
|
||||||
{subHeadline}
|
{subHeadline}
|
||||||
</Text>
|
</Text>
|
||||||
<PaperSeparator className="w-3/4" type="single" />
|
<Separator className="w-3/4" type="single" />
|
||||||
</>
|
</>
|
||||||
) : <PaperSeparator className="w-3/4" type="single" />}
|
) : <Separator className="w-3/4" type="single" />}
|
||||||
</VStack>
|
</VStack>
|
||||||
|
|
||||||
<Text size="m" type="body">
|
<Text size="m" type="body">
|
||||||
+1
-1
@@ -6,7 +6,7 @@ import { Link } from '../Link/Link';
|
|||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
const meta: Meta<typeof Breadcrumbs> = {
|
const meta: Meta<typeof Breadcrumbs> = {
|
||||||
title: 'components/generic/Breadcrumbs',
|
title: 'components/Breadcrumbs',
|
||||||
component: Breadcrumbs,
|
component: Breadcrumbs,
|
||||||
tags: ['autodocs'],
|
tags: ['autodocs'],
|
||||||
};
|
};
|
||||||
File renamed without changes.
File renamed without changes.
+12
-12
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { PaperButton } from './PaperButton';
|
import { Button } from './Button';
|
||||||
|
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
@@ -15,9 +15,9 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|||||||
* - Accessible interaction patterns via react-aria
|
* - Accessible interaction patterns via react-aria
|
||||||
* - Responsive hover and focus states
|
* - Responsive hover and focus states
|
||||||
*/
|
*/
|
||||||
const meta: Meta<typeof PaperButton> = {
|
const meta: Meta<typeof Button> = {
|
||||||
title: 'components/paper/PaperButton',
|
title: 'components/Button',
|
||||||
component: PaperButton,
|
component: Button,
|
||||||
parameters: {
|
parameters: {
|
||||||
layout: 'centered',
|
layout: 'centered',
|
||||||
docs: {
|
docs: {
|
||||||
@@ -111,18 +111,18 @@ export const LongText: Story = {
|
|||||||
export const AllVariants: Story = {
|
export const AllVariants: Story = {
|
||||||
render: () => (
|
render: () => (
|
||||||
<div className="flex flex-col gap-4 items-start">
|
<div className="flex flex-col gap-4 items-start">
|
||||||
<PaperButton variant="default" onPress={() => console.log('Default')}>
|
<Button variant="default" onPress={() => console.log('Default')}>
|
||||||
Default Style
|
Default Style
|
||||||
</PaperButton>
|
</Button>
|
||||||
<PaperButton variant="primary" onPress={() => console.log('Primary')}>
|
<Button variant="primary" onPress={() => console.log('Primary')}>
|
||||||
Primary Style
|
Primary Style
|
||||||
</PaperButton>
|
</Button>
|
||||||
<PaperButton variant="secondary" onPress={() => console.log('Secondary')}>
|
<Button variant="secondary" onPress={() => console.log('Secondary')}>
|
||||||
Secondary Style
|
Secondary Style
|
||||||
</PaperButton>
|
</Button>
|
||||||
<PaperButton isDisabled>
|
<Button isDisabled>
|
||||||
Disabled State
|
Disabled State
|
||||||
</PaperButton>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
+23
-23
@@ -1,27 +1,27 @@
|
|||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { PaperButton } from './PaperButton';
|
import { Button } from './Button';
|
||||||
import { render, screen } from '../../../test-utils';
|
import { render, screen } from '../../test-utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test suite for the newspaper-style PaperButton component
|
* Test suite for the newspaper-style Button component
|
||||||
*/
|
*/
|
||||||
describe('components/paper/PaperButton/PaperButton', () => {
|
describe('components/paper/Button/Button', () => {
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
children: 'Test PaperButton',
|
children: 'Test Button',
|
||||||
};
|
};
|
||||||
|
|
||||||
it('renders correctly with default props', () => {
|
it('renders correctly with default props', () => {
|
||||||
render(<PaperButton {...defaultProps} />);
|
render(<Button {...defaultProps} />);
|
||||||
|
|
||||||
const button = screen.getByRole('button', { name: 'Test PaperButton' });
|
const button = screen.getByRole('button', { name: 'Test Button' });
|
||||||
expect(button).toBeInTheDocument();
|
expect(button).toBeInTheDocument();
|
||||||
expect(button).toHaveTextContent('Test PaperButton');
|
expect(button).toHaveTextContent('Test Button');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('applies correct styling classes', () => {
|
it('applies correct styling classes', () => {
|
||||||
render(<PaperButton {...defaultProps} />);
|
render(<Button {...defaultProps} />);
|
||||||
|
|
||||||
const button = screen.getByRole('button');
|
const button = screen.getByRole('button');
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ describe('components/paper/PaperButton/PaperButton', () => {
|
|||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
const handlePress = jest.fn();
|
const handlePress = jest.fn();
|
||||||
|
|
||||||
render(<PaperButton onPress={handlePress}>{defaultProps.children}</PaperButton>);
|
render(<Button onPress={handlePress}>{defaultProps.children}</Button>);
|
||||||
|
|
||||||
const button = screen.getByRole('button');
|
const button = screen.getByRole('button');
|
||||||
await user.click(button);
|
await user.click(button);
|
||||||
@@ -47,19 +47,19 @@ describe('components/paper/PaperButton/PaperButton', () => {
|
|||||||
|
|
||||||
it('supports different variants', () => {
|
it('supports different variants', () => {
|
||||||
const { rerender } = render(
|
const { rerender } = render(
|
||||||
<PaperButton variant="primary">{defaultProps.children}</PaperButton>
|
<Button variant="primary">{defaultProps.children}</Button>
|
||||||
);
|
);
|
||||||
|
|
||||||
let button = screen.getByRole('button');
|
let button = screen.getByRole('button');
|
||||||
expect(button).toHaveClass('bg-primary');
|
expect(button).toHaveClass('bg-primary');
|
||||||
|
|
||||||
rerender(<PaperButton variant="secondary">{defaultProps.children}</PaperButton>);
|
rerender(<Button variant="secondary">{defaultProps.children}</Button>);
|
||||||
button = screen.getByRole('button');
|
button = screen.getByRole('button');
|
||||||
expect(button).toHaveClass('bg-secondary');
|
expect(button).toHaveClass('bg-secondary');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles disabled state correctly', () => {
|
it('handles disabled state correctly', () => {
|
||||||
render(<PaperButton isDisabled>{defaultProps.children}</PaperButton>);
|
render(<Button isDisabled>{defaultProps.children}</Button>);
|
||||||
|
|
||||||
const button = screen.getByRole('button');
|
const button = screen.getByRole('button');
|
||||||
expect(button).toBeDisabled();
|
expect(button).toBeDisabled();
|
||||||
@@ -72,9 +72,9 @@ describe('components/paper/PaperButton/PaperButton', () => {
|
|||||||
const handlePress = jest.fn();
|
const handlePress = jest.fn();
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<PaperButton isDisabled onPress={handlePress}>
|
<Button isDisabled onPress={handlePress}>
|
||||||
{defaultProps.children}
|
{defaultProps.children}
|
||||||
</PaperButton>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|
||||||
const button = screen.getByRole('button');
|
const button = screen.getByRole('button');
|
||||||
@@ -86,7 +86,7 @@ describe('components/paper/PaperButton/PaperButton', () => {
|
|||||||
it('supports custom className prop', () => {
|
it('supports custom className prop', () => {
|
||||||
const customClass = 'custom-button-class';
|
const customClass = 'custom-button-class';
|
||||||
|
|
||||||
render(<PaperButton className={customClass}>{defaultProps.children}</PaperButton>);
|
render(<Button className={customClass}>{defaultProps.children}</Button>);
|
||||||
|
|
||||||
const button = screen.getByRole('button');
|
const button = screen.getByRole('button');
|
||||||
expect(button).toHaveClass(customClass);
|
expect(button).toHaveClass(customClass);
|
||||||
@@ -95,27 +95,27 @@ describe('components/paper/PaperButton/PaperButton', () => {
|
|||||||
it('supports data-testid prop', () => {
|
it('supports data-testid prop', () => {
|
||||||
const testId = 'my-test-button';
|
const testId = 'my-test-button';
|
||||||
|
|
||||||
render(<PaperButton data-testid={testId}>{defaultProps.children}</PaperButton>);
|
render(<Button data-testid={testId}>{defaultProps.children}</Button>);
|
||||||
|
|
||||||
const button = screen.getByTestId(testId);
|
const button = screen.getByTestId(testId);
|
||||||
expect(button).toBeInTheDocument();
|
expect(button).toBeInTheDocument();
|
||||||
expect(button).toHaveTextContent('Test PaperButton');
|
expect(button).toHaveTextContent('Test Button');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('only accepts string children', () => {
|
it('only accepts string children', () => {
|
||||||
// This test verifies TypeScript constraint - should compile without error
|
// This test verifies TypeScript constraint - should compile without error
|
||||||
render(<PaperButton>Simple string</PaperButton>);
|
render(<Button>Simple string</Button>);
|
||||||
expect(screen.getByText('Simple string')).toBeInTheDocument();
|
expect(screen.getByText('Simple string')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('maintains accessibility attributes', () => {
|
it('maintains accessibility attributes', () => {
|
||||||
render(
|
render(
|
||||||
<PaperButton
|
<Button
|
||||||
aria-describedby="button-description"
|
aria-describedby="button-description"
|
||||||
aria-label="Custom accessibility label"
|
aria-label="Custom accessibility label"
|
||||||
>
|
>
|
||||||
{defaultProps.children}
|
{defaultProps.children}
|
||||||
</PaperButton>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|
||||||
const button = screen.getByRole('button');
|
const button = screen.getByRole('button');
|
||||||
@@ -127,7 +127,7 @@ describe('components/paper/PaperButton/PaperButton', () => {
|
|||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
const handlePress = jest.fn();
|
const handlePress = jest.fn();
|
||||||
|
|
||||||
render(<PaperButton onPress={handlePress}>{defaultProps.children}</PaperButton>);
|
render(<Button onPress={handlePress}>{defaultProps.children}</Button>);
|
||||||
|
|
||||||
const button = screen.getByRole('button');
|
const button = screen.getByRole('button');
|
||||||
button.focus();
|
button.focus();
|
||||||
@@ -142,7 +142,7 @@ describe('components/paper/PaperButton/PaperButton', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('renders string content correctly', () => {
|
it('renders string content correctly', () => {
|
||||||
render(<PaperButton>Simple text content</PaperButton>);
|
render(<Button>Simple text content</Button>);
|
||||||
expect(screen.getByText('Simple text content')).toBeInTheDocument();
|
expect(screen.getByText('Simple text content')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
+7
-7
@@ -5,9 +5,9 @@ import type { ClassNameProps, TestIdProps } from '@ermnvldmr/stl';
|
|||||||
import type { AriaButtonProps } from 'react-aria';
|
import type { AriaButtonProps } from 'react-aria';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Props for the newspaper-style PaperButton component.
|
* Props for the Button component.
|
||||||
*/
|
*/
|
||||||
export interface PaperButtonProps extends AriaButtonProps, ClassNameProps, TestIdProps {
|
export interface ButtonProps extends AriaButtonProps, ClassNameProps, TestIdProps {
|
||||||
/** The button text content */
|
/** The button text content */
|
||||||
children: string;
|
children: string;
|
||||||
/** Visual style variant */
|
/** Visual style variant */
|
||||||
@@ -15,7 +15,7 @@ export interface PaperButtonProps extends AriaButtonProps, ClassNameProps, TestI
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A newspaper-style button component with italic Lato font and underlined text.
|
* A button component with italic Lato font and underlined text.
|
||||||
*
|
*
|
||||||
* Features:
|
* Features:
|
||||||
* - Rectangular design with subtle background
|
* - Rectangular design with subtle background
|
||||||
@@ -26,18 +26,18 @@ export interface PaperButtonProps extends AriaButtonProps, ClassNameProps, TestI
|
|||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```tsx
|
* ```tsx
|
||||||
* <PaperButton onPress={() => console.log('Clicked!')}>
|
* <Button onPress={() => console.log('Clicked!')}>
|
||||||
* Explore all the articles
|
* Explore all the articles
|
||||||
* </PaperButton>
|
* </Button>
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export const PaperButton = memo(function PaperButton({
|
export const Button = memo(function Button({
|
||||||
children,
|
children,
|
||||||
variant = 'default',
|
variant = 'default',
|
||||||
className = '',
|
className = '',
|
||||||
'data-testid': testId,
|
'data-testid': testId,
|
||||||
...buttonProps
|
...buttonProps
|
||||||
}: PaperButtonProps) {
|
}: ButtonProps) {
|
||||||
const ref = React.useRef<HTMLButtonElement>(null);
|
const ref = React.useRef<HTMLButtonElement>(null);
|
||||||
const { buttonProps: ariaButtonProps } = useButton(buttonProps, ref);
|
const { buttonProps: ariaButtonProps } = useButton(buttonProps, ref);
|
||||||
|
|
||||||
+1
-1
@@ -8,7 +8,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|||||||
|
|
||||||
|
|
||||||
const meta: Meta<typeof Container> = {
|
const meta: Meta<typeof Container> = {
|
||||||
title: 'components/generic/Container',
|
title: 'components/Container',
|
||||||
component: Container,
|
component: Container,
|
||||||
tags: ['autodocs'],
|
tags: ['autodocs'],
|
||||||
argTypes: {
|
argTypes: {
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
+2
-2
@@ -7,7 +7,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|||||||
|
|
||||||
|
|
||||||
const meta: Meta<typeof Header> = {
|
const meta: Meta<typeof Header> = {
|
||||||
title: 'Generic/Header',
|
title: 'components/Header',
|
||||||
component: Header,
|
component: Header,
|
||||||
tags: ['autodocs'],
|
tags: ['autodocs'],
|
||||||
argTypes: {
|
argTypes: {
|
||||||
@@ -56,4 +56,4 @@ export const Overrides: Story = {
|
|||||||
<Header italic underline level={6}>H6 with Modifiers</Header>
|
<Header italic underline level={6}>H6 with Modifiers</Header>
|
||||||
</VStack>
|
</VStack>
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
File renamed without changes.
+1
-1
@@ -4,7 +4,7 @@ import { Link } from './Link';
|
|||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
const meta: Meta<typeof Link> = {
|
const meta: Meta<typeof Link> = {
|
||||||
title: 'components/generic/Link',
|
title: 'components/Link',
|
||||||
component: Link,
|
component: Link,
|
||||||
tags: ['autodocs'],
|
tags: ['autodocs'],
|
||||||
argTypes: {
|
argTypes: {
|
||||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,254 @@
|
|||||||
|
import { Markdown } from './Markdown';
|
||||||
|
|
||||||
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata for the Markdown component stories.
|
||||||
|
*/
|
||||||
|
const meta: Meta<typeof Markdown> = {
|
||||||
|
title: 'components/Markdown',
|
||||||
|
component: Markdown,
|
||||||
|
tags: ['autodocs'],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type definition for Markdown component stories.
|
||||||
|
*/
|
||||||
|
type Story = StoryObj<typeof Markdown>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default story showcasing various HTML elements to verify typography.
|
||||||
|
*/
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
children: (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
This is a <strong>Markdown</strong> component that uses Tailwind CSS Typography (prose)
|
||||||
|
to render content with consistent styling.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
It is designed to be used with rendered HTML from markdown processors,
|
||||||
|
but works with any HTML content that needs standard document styling.
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Showcasing all heading levels.
|
||||||
|
*/
|
||||||
|
export const Typography: Story = {
|
||||||
|
args: {
|
||||||
|
children: (
|
||||||
|
<>
|
||||||
|
<h1>Heading Level 1</h1>
|
||||||
|
<p>Paragraph text following an H1.</p>
|
||||||
|
<h2>Heading Level 2</h2>
|
||||||
|
<p>Paragraph text following an H2.</p>
|
||||||
|
<h3>Heading Level 3</h3>
|
||||||
|
<p>Paragraph text following an H3.</p>
|
||||||
|
<h4>Heading Level 4</h4>
|
||||||
|
<p>Paragraph text following an H4.</p>
|
||||||
|
<h5>Heading Level 5</h5>
|
||||||
|
<p>Paragraph text following an H5.</p>
|
||||||
|
<h6>Heading Level 6</h6>
|
||||||
|
<p>Paragraph text following an H6.</p>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Showcasing ordered and unordered lists.
|
||||||
|
*/
|
||||||
|
export const Lists: Story = {
|
||||||
|
args: {
|
||||||
|
children: (
|
||||||
|
<>
|
||||||
|
<h3>Unordered List</h3>
|
||||||
|
<ul>
|
||||||
|
<li>First item</li>
|
||||||
|
<li>Second item
|
||||||
|
<ul>
|
||||||
|
<li>Nested item A</li>
|
||||||
|
<li>Nested item B</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>Third item</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Ordered List</h3>
|
||||||
|
<ol>
|
||||||
|
<li>Step one</li>
|
||||||
|
<li>Step two
|
||||||
|
<ol>
|
||||||
|
<li>Sub-step 2.1</li>
|
||||||
|
<li>Sub-step 2.2</li>
|
||||||
|
</ol>
|
||||||
|
</li>
|
||||||
|
<li>Step three</li>
|
||||||
|
</ol>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Showcasing blockquotes.
|
||||||
|
*/
|
||||||
|
export const Quotes: Story = {
|
||||||
|
args: {
|
||||||
|
children: (
|
||||||
|
<>
|
||||||
|
<p>Before the quote.</p>
|
||||||
|
<blockquote>
|
||||||
|
"Design is not just what it looks like and feels like. Design is how it works."
|
||||||
|
<footer>— Steve Jobs</footer>
|
||||||
|
</blockquote>
|
||||||
|
<p>After the quote.</p>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Showcasing code and pre blocks.
|
||||||
|
*/
|
||||||
|
export const Code: Story = {
|
||||||
|
args: {
|
||||||
|
children: (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
You can use <code>inline code</code> for small snippets.
|
||||||
|
</p>
|
||||||
|
<pre>
|
||||||
|
<code>{`function hello() {
|
||||||
|
console.log("Hello, world!");
|
||||||
|
}`}</code>
|
||||||
|
</pre>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Showcasing tables.
|
||||||
|
*/
|
||||||
|
export const Tables: Story = {
|
||||||
|
args: {
|
||||||
|
children: (
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Feature</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Status</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Typography</td>
|
||||||
|
<td>Garamond and Lato fonts</td>
|
||||||
|
<td>✅ Done</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Dark Mode</td>
|
||||||
|
<td>Automatic inversion</td>
|
||||||
|
<td>✅ Done</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Responsive</td>
|
||||||
|
<td>Fluid layouts</td>
|
||||||
|
<td>✅ Done</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "Kitchen Sink" containing everything.
|
||||||
|
*/
|
||||||
|
export const KitchenSink: Story = {
|
||||||
|
args: {
|
||||||
|
children: (
|
||||||
|
<>
|
||||||
|
<h1>The Art of Markdown Rendering</h1>
|
||||||
|
<p className="lead">
|
||||||
|
A lead paragraph that stands out and sets the tone for the rest of the document.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Markdown is a lightweight markup language with plain-text-formatting syntax.
|
||||||
|
Its design allows it to be converted to many output formats, but the name
|
||||||
|
originally referred to the tool for converting it to HTML.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Key Features</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Easy to read</strong> - The source text looks like the final output.</li>
|
||||||
|
<li><strong>Easy to write</strong> - Minimal syntax overhead.</li>
|
||||||
|
<li><strong>Versatile</strong> - Supports many elements like <em>italics</em>, <del>strikethrough</del>, and <code>code</code>.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Example Code Block</h3>
|
||||||
|
<pre>
|
||||||
|
<code>{`import { Markdown } from '@ermnvldmr/ui';
|
||||||
|
|
||||||
|
export const MyPage = () => (
|
||||||
|
<Markdown>
|
||||||
|
<h1>Welcome</h1>
|
||||||
|
<p>This is styled automatically.</p>
|
||||||
|
</Markdown>
|
||||||
|
);`}</code>
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<blockquote>
|
||||||
|
"Simplicity is the ultimate sophistication."
|
||||||
|
<br />
|
||||||
|
— Leonardo da Vinci
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<h2>Data Representation</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Language</th>
|
||||||
|
<th>Difficulty</th>
|
||||||
|
<th>Usage</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>TypeScript</td>
|
||||||
|
<td>Moderate</td>
|
||||||
|
<td>Frontend/Backend</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Rust</td>
|
||||||
|
<td>High</td>
|
||||||
|
<td>Systems</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Python</td>
|
||||||
|
<td>Low</td>
|
||||||
|
<td>Data Science</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>Media Elements</h3>
|
||||||
|
<p>Images should be rounded and have a nice shadow.</p>
|
||||||
|
<img alt="Coding" src="https://images.unsplash.com/photo-1498050108023-c5249f4df085?auto=format&fit=crop&w=1200&q=80" />
|
||||||
|
|
||||||
|
<p>Conclusion: We've built a robust system for rendering content.</p>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { cn } from '@ermnvldmr/stl';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Props for the Markdown component.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Props for the Markdown component.
|
||||||
|
*/
|
||||||
|
export interface MarkdownProps {
|
||||||
|
/** The content to be rendered, typically HTML from a markdown processor. */
|
||||||
|
children: React.ReactNode;
|
||||||
|
/** Optional additional class names. */
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Markdown component provides a styled container for rendered markdown content.
|
||||||
|
* It uses the Tailwind CSS Typography plugin (prose) with custom project styles.
|
||||||
|
*
|
||||||
|
* @param props - The component props.
|
||||||
|
* @param props.children
|
||||||
|
* @param props.className
|
||||||
|
* @returns A React element.
|
||||||
|
* @example
|
||||||
|
* ```tsx
|
||||||
|
* <Markdown>
|
||||||
|
* <h1>Title</h1>
|
||||||
|
* <p>Content goes here.</p>
|
||||||
|
* </Markdown>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export const Markdown: React.FC<MarkdownProps> = ({ children, className }) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
+16
-16
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { PaperSeparator } from './PaperSeparator';
|
import { Separator } from './Separator';
|
||||||
|
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
@@ -15,9 +15,9 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|||||||
* - 2px stroke width following print design standards
|
* - 2px stroke width following print design standards
|
||||||
* - Semantic div elements with proper ARIA separator role for accessibility
|
* - Semantic div elements with proper ARIA separator role for accessibility
|
||||||
*/
|
*/
|
||||||
const meta: Meta<typeof PaperSeparator> = {
|
const meta: Meta<typeof Separator> = {
|
||||||
title: 'components/paper/PaperSeparator',
|
title: 'components/Separator',
|
||||||
component: PaperSeparator,
|
component: Separator,
|
||||||
parameters: {
|
parameters: {
|
||||||
layout: 'centered',
|
layout: 'centered',
|
||||||
docs: {
|
docs: {
|
||||||
@@ -56,7 +56,7 @@ const meta: Meta<typeof PaperSeparator> = {
|
|||||||
|
|
||||||
export default meta;
|
export default meta;
|
||||||
/**
|
/**
|
||||||
* Story type for PaperSeparator component stories.
|
* Story type for Separator component stories.
|
||||||
*/
|
*/
|
||||||
type Story = StoryObj<typeof meta>;
|
type Story = StoryObj<typeof meta>;
|
||||||
|
|
||||||
@@ -201,7 +201,7 @@ export const VerticalDouble: Story = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PaperSeparator without thinned ends showing clean, sharp line edges.
|
* Separator without thinned ends showing clean, sharp line edges.
|
||||||
* Compare with default to see the gradient fade difference.
|
* Compare with default to see the gradient fade difference.
|
||||||
*/
|
*/
|
||||||
export const NonThinned: Story = {
|
export const NonThinned: Story = {
|
||||||
@@ -232,15 +232,15 @@ export const AllColors: Story = {
|
|||||||
<div className="space-y-8 w-96">
|
<div className="space-y-8 w-96">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-medium mb-2">Black</h3>
|
<h3 className="text-sm font-medium mb-2">Black</h3>
|
||||||
<PaperSeparator color="black" />
|
<Separator color="black" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-medium mb-2">Outline (Default for Double)</h3>
|
<h3 className="text-sm font-medium mb-2">Outline (Default for Double)</h3>
|
||||||
<PaperSeparator color="outline" />
|
<Separator color="outline" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-medium mb-2">Outline Light (Default for Single)</h3>
|
<h3 className="text-sm font-medium mb-2">Outline Light (Default for Single)</h3>
|
||||||
<PaperSeparator color="outline-light" />
|
<Separator color="outline-light" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
@@ -255,11 +255,11 @@ export const AllTypes: Story = {
|
|||||||
<div className="space-y-8 w-96">
|
<div className="space-y-8 w-96">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-medium mb-2">Single (Default: Outline Light)</h3>
|
<h3 className="text-sm font-medium mb-2">Single (Default: Outline Light)</h3>
|
||||||
<PaperSeparator type="single" />
|
<Separator type="single" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-medium mb-2">Double (Default: Outline)</h3>
|
<h3 className="text-sm font-medium mb-2">Double (Default: Outline)</h3>
|
||||||
<PaperSeparator type="double" />
|
<Separator type="double" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
@@ -274,11 +274,11 @@ export const ThinnedComparison: Story = {
|
|||||||
<div className="space-y-8 w-96">
|
<div className="space-y-8 w-96">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-medium mb-2">With Thinned Ends (Default)</h3>
|
<h3 className="text-sm font-medium mb-2">With Thinned Ends (Default)</h3>
|
||||||
<PaperSeparator thinned />
|
<Separator thinned />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-medium mb-2">Without Thinned Ends</h3>
|
<h3 className="text-sm font-medium mb-2">Without Thinned Ends</h3>
|
||||||
<PaperSeparator thinned={false} />
|
<Separator thinned={false} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
@@ -296,7 +296,7 @@ export const VerticalShowcase: Story = {
|
|||||||
Short
|
Short
|
||||||
</div>
|
</div>
|
||||||
<div className="px-2">
|
<div className="px-2">
|
||||||
<PaperSeparator direction="vertical" />
|
<Separator direction="vertical" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-center w-20 text-xs text-muted-foreground">
|
<div className="flex items-center justify-center w-20 text-xs text-muted-foreground">
|
||||||
Content
|
Content
|
||||||
@@ -308,7 +308,7 @@ export const VerticalShowcase: Story = {
|
|||||||
Medium
|
Medium
|
||||||
</div>
|
</div>
|
||||||
<div className="px-2">
|
<div className="px-2">
|
||||||
<PaperSeparator direction="vertical" type="double" />
|
<Separator direction="vertical" type="double" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-center w-20 text-xs text-muted-foreground">
|
<div className="flex items-center justify-center w-20 text-xs text-muted-foreground">
|
||||||
Content
|
Content
|
||||||
@@ -320,7 +320,7 @@ export const VerticalShowcase: Story = {
|
|||||||
Tall
|
Tall
|
||||||
</div>
|
</div>
|
||||||
<div className="px-2">
|
<div className="px-2">
|
||||||
<PaperSeparator color="black" direction="vertical" />
|
<Separator color="black" direction="vertical" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-center w-20 text-xs text-muted-foreground">
|
<div className="flex items-center justify-center w-20 text-xs text-muted-foreground">
|
||||||
Content
|
Content
|
||||||
+10
-10
@@ -1,15 +1,15 @@
|
|||||||
import { cn } from '@ermnvldmr/stl';
|
import { cn } from '@ermnvldmr/stl';
|
||||||
import React, { memo } from 'react';
|
import React, { memo } from 'react';
|
||||||
|
|
||||||
import { VStack } from '../../generic/VStack/VStack';
|
import { VStack } from '../VStack/VStack';
|
||||||
|
|
||||||
import type { ClassNameProps, TestIdProps } from '@ermnvldmr/stl';
|
import type { ClassNameProps, TestIdProps } from '@ermnvldmr/stl';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Props for the newspaper-style PaperSeparator component.
|
* Props for the Separator component.
|
||||||
*/
|
*/
|
||||||
export interface PaperSeparatorProps extends ClassNameProps, TestIdProps {
|
export interface SeparatorProps extends ClassNameProps, TestIdProps {
|
||||||
/** Type of separator line styling */
|
/** Type of separator line styling */
|
||||||
type?: 'single' | 'double';
|
type?: 'single' | 'double';
|
||||||
/** Color variant following newspaper design patterns */
|
/** Color variant following newspaper design patterns */
|
||||||
@@ -21,7 +21,7 @@ export interface PaperSeparatorProps extends ClassNameProps, TestIdProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A newspaper-style separator component for dividing content sections.
|
* A separator component for dividing content sections.
|
||||||
*
|
*
|
||||||
* Features:
|
* Features:
|
||||||
* - Single or double line variants inspired by newspaper layout traditions
|
* - Single or double line variants inspired by newspaper layout traditions
|
||||||
@@ -37,26 +37,26 @@ export interface PaperSeparatorProps extends ClassNameProps, TestIdProps {
|
|||||||
* @example
|
* @example
|
||||||
* ```tsx
|
* ```tsx
|
||||||
* // Basic horizontal separator
|
* // Basic horizontal separator
|
||||||
* <PaperSeparator />
|
* <Separator />
|
||||||
*
|
*
|
||||||
* // Double-line separator with black color
|
* // Double-line separator with black color
|
||||||
* <PaperSeparator type="double" color="black" />
|
* <Separator type="double" color="black" />
|
||||||
*
|
*
|
||||||
* // Vertical separator without thinned ends
|
* // Vertical separator without thinned ends
|
||||||
* <PaperSeparator direction="vertical" thinned={false} />
|
* <Separator direction="vertical" thinned={false} />
|
||||||
*
|
*
|
||||||
* // Light separator for subtle content division
|
* // Light separator for subtle content division
|
||||||
* <PaperSeparator color="outline-light" />
|
* <Separator color="outline-light" />
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export const PaperSeparator = memo(function PaperSeparator({
|
export const Separator = memo(function Separator({
|
||||||
type = 'single',
|
type = 'single',
|
||||||
color,
|
color,
|
||||||
direction = 'horizontal',
|
direction = 'horizontal',
|
||||||
thinned = true,
|
thinned = true,
|
||||||
className,
|
className,
|
||||||
'data-testid': testId,
|
'data-testid': testId,
|
||||||
}: PaperSeparatorProps) {
|
}: SeparatorProps) {
|
||||||
// Default color based on type as specified
|
// Default color based on type as specified
|
||||||
const defaultColor = type === 'single' ? 'outline-light' : 'outline';
|
const defaultColor = type === 'single' ? 'outline-light' : 'outline';
|
||||||
const finalColor = color ?? defaultColor;
|
const finalColor = color ?? defaultColor;
|
||||||
+1
-1
@@ -12,7 +12,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|||||||
* through semantic prop names that map to Tailwind CSS utility classes.
|
* through semantic prop names that map to Tailwind CSS utility classes.
|
||||||
*/
|
*/
|
||||||
const meta: Meta<typeof Stack> = {
|
const meta: Meta<typeof Stack> = {
|
||||||
title: 'components/generic/Stack',
|
title: 'components/Stack',
|
||||||
component: Stack,
|
component: Stack,
|
||||||
parameters: {
|
parameters: {
|
||||||
layout: 'centered',
|
layout: 'centered',
|
||||||
File renamed without changes.
+1
-1
@@ -7,7 +7,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|||||||
|
|
||||||
|
|
||||||
const meta: Meta<typeof Text> = {
|
const meta: Meta<typeof Text> = {
|
||||||
title: 'components/generic/Text',
|
title: 'components/Text',
|
||||||
component: Text,
|
component: Text,
|
||||||
tags: ['autodocs'],
|
tags: ['autodocs'],
|
||||||
argTypes: {
|
argTypes: {
|
||||||
File renamed without changes.
File renamed without changes.
@@ -6,6 +6,7 @@ export * from './Text/Text';
|
|||||||
export * from './VStack/VStack';
|
export * from './VStack/VStack';
|
||||||
export * from './Link/Link';
|
export * from './Link/Link';
|
||||||
export * from './Breadcrumbs/Breadcrumbs';
|
export * from './Breadcrumbs/Breadcrumbs';
|
||||||
|
export * from './Markdown/Markdown';
|
||||||
|
|
||||||
export type { StackProps } from './Stack/Stack';
|
export type { StackProps } from './Stack/Stack';
|
||||||
export type { ContainerProps, ContainerBackground, ContainerMaxWidth, ContainerRounded } from './Container/Container';
|
export type { ContainerProps, ContainerBackground, ContainerMaxWidth, ContainerRounded } from './Container/Container';
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export * from './PaperButton/PaperButton';
|
|
||||||
export * from './PaperSeparator/PaperSeparator';
|
|
||||||
export * from './PaperArticle/PaperArticle';
|
|
||||||
@@ -1,63 +1,10 @@
|
|||||||
@import url('https://fonts.googleapis.com/css2?family=EB+Garamond:ital,wght@0,400..800;1,400..800&family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=EB+Garamond:ital,wght@0,400..800;1,400..800&family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap');
|
||||||
|
|
||||||
@import 'tailwindcss';
|
@import 'tailwindcss';
|
||||||
@import 'tw-animate-css';
|
@import 'tw-animate-css';
|
||||||
|
|
||||||
@custom-variant dark (&:where(.dark, .dark *));
|
@custom-variant dark (&:where(.dark, .dark *));
|
||||||
|
|
||||||
/*
|
|
||||||
@layer base defaults inspired by shadcn UI Typography
|
|
||||||
Provides opinionated, utility-driven defaults for HTML elements.
|
|
||||||
*/
|
|
||||||
@layer base {
|
|
||||||
/* Headings */
|
|
||||||
h1 {
|
|
||||||
@apply scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
@apply mt-10 scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight transition-colors first:mt-0;
|
|
||||||
}
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6 {
|
|
||||||
@apply mt-8 scroll-m-20 text-2xl font-semibold tracking-tight;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Paragraphs */
|
|
||||||
p {
|
|
||||||
@apply leading-7 [&:not(:first-child)]:mt-6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Links */
|
|
||||||
a {
|
|
||||||
@apply text-ring font-medium underline underline-offset-2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Blockquotes */
|
|
||||||
blockquote {
|
|
||||||
@apply mt-6 border-l-2 pl-6 italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Code */
|
|
||||||
code {
|
|
||||||
@apply bg-muted rounded px-1 font-mono text-sm;
|
|
||||||
}
|
|
||||||
pre {
|
|
||||||
@apply bg-muted overflow-auto rounded-lg p-4 font-mono text-sm;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Images and Media */
|
|
||||||
img,
|
|
||||||
video {
|
|
||||||
@apply max-w-full rounded-lg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Horizontal rules */
|
|
||||||
hr {
|
|
||||||
@apply border-border my-6 border-t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
--font-sans: 'Lato', sans-serif;
|
--font-sans: 'Lato', sans-serif;
|
||||||
--font-serif: 'EB Garamond', serif;
|
--font-serif: 'EB Garamond', serif;
|
||||||
@@ -186,24 +133,11 @@
|
|||||||
--rb-muted-text: var(--rb-color-neutral-600);
|
--rb-muted-text: var(--rb-color-neutral-600);
|
||||||
|
|
||||||
/* Charts */
|
/* Charts */
|
||||||
--chart-1: var(--rb-color-blue-500);
|
--rb-chart-1: var(--rb-color-blue-500);
|
||||||
--chart-2: var(--rb-color-green-500);
|
--rb--chart-2: var(--rb-color-green-500);
|
||||||
--chart-3: var(--rb-color-yellow-500);
|
--rb-chart-3: var(--rb-color-yellow-500);
|
||||||
--chart-4: var(--rb-color-orange-500);
|
--rb-chart-4: var(--rb-color-orange-500);
|
||||||
--chart-5: var(--rb-color-red-500);
|
--rb-chart-5: var(--rb-color-red-500);
|
||||||
|
|
||||||
/* Sidebar */
|
|
||||||
--sidebar: var(--rb-color-neutral-50);
|
|
||||||
--sidebar-foreground: var(--rb-color-neutral-950);
|
|
||||||
--sidebar-primary: var(--rb-color-cyan-200);
|
|
||||||
--sidebar-primary-foreground: var(--rb-color-cyan-950);
|
|
||||||
--sidebar-accent: var(--rb-color-violet-200);
|
|
||||||
--sidebar-accent-foreground: var(--rb-color-violet-950);
|
|
||||||
--sidebar-border: var(--rb-color-neutral-500);
|
|
||||||
--sidebar-ring: var(--rb-color-cyan-700);
|
|
||||||
|
|
||||||
/* Radii */
|
|
||||||
--radius: 0.625rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
@@ -231,79 +165,14 @@
|
|||||||
--rb-muted-text: var(--rb-color-neutral-100);
|
--rb-muted-text: var(--rb-color-neutral-100);
|
||||||
|
|
||||||
/* Charts */
|
/* Charts */
|
||||||
--chart-1: var(--rb-color-blue-400);
|
--rb-chart-1: var(--rb-color-blue-400);
|
||||||
--chart-2: var(--rb-color-green-400);
|
--rb-chart-2: var(--rb-color-green-400);
|
||||||
--chart-3: var(--rb-color-yellow-400);
|
--rb-chart-3: var(--rb-color-yellow-400);
|
||||||
--chart-4: var(--rb-color-orange-400);
|
--rb-chart-4: var(--rb-color-orange-400);
|
||||||
--chart-5: var(--rb-color-red-400);
|
--rb-chart-5: var(--rb-color-red-400);
|
||||||
|
|
||||||
/* Sidebar */
|
|
||||||
--sidebar: var(--rb-color-neutral-950);
|
|
||||||
--sidebar-foreground: var(--rb-color-neutral-50);
|
|
||||||
--sidebar-primary: var(--rb-color-cyan-800);
|
|
||||||
--sidebar-primary-foreground: var(--rb-color-cyan-50);
|
|
||||||
--sidebar-accent: var(--rb-color-violet-800);
|
|
||||||
--sidebar-accent-foreground: var(--rb-color-violet-50);
|
|
||||||
--sidebar-border: var(--rb-color-neutral-500);
|
|
||||||
--sidebar-ring: var(--rb-color-cyan-700);
|
|
||||||
}
|
|
||||||
|
|
||||||
@utility vstack {
|
|
||||||
@apply flex flex-col;
|
|
||||||
}
|
|
||||||
|
|
||||||
@utility hstack {
|
|
||||||
@apply flex flex-row;
|
|
||||||
}
|
|
||||||
|
|
||||||
@theme inline {
|
|
||||||
--radius-sm: calc(var(--radius) - 4px);
|
|
||||||
--radius-md: calc(var(--radius) - 2px);
|
|
||||||
--radius-lg: var(--radius);
|
|
||||||
--radius-xl: calc(var(--radius) + 4px);
|
|
||||||
|
|
||||||
--color-background: var(--rb-base);
|
|
||||||
--color-foreground: var(--rb-text);
|
|
||||||
--color-card: var(--rb-container-base);
|
|
||||||
--color-card-foreground: var(--rb-container-text);
|
|
||||||
--color-popover: var(--rb-container-base);
|
|
||||||
--color-popover-foreground: var(--rb-container-text);
|
|
||||||
|
|
||||||
--color-primary: var(--rb-primary-base);
|
|
||||||
--color-primary-foreground: var(--rb-primary-text);
|
|
||||||
--color-secondary: var(--rb-secondary-base);
|
|
||||||
--color-secondary-foreground: var(--rb-secondary-text);
|
|
||||||
--color-accent: var(--rb-tertiary-base);
|
|
||||||
--color-accent-foreground: var(--rb-tertiary-text);
|
|
||||||
--color-destructive: var(--rb-error-base);
|
|
||||||
--color-destructive-foreground: var(--rb-error-text);
|
|
||||||
|
|
||||||
--color-muted: var(--rb-muted-base);
|
|
||||||
--color-muted-foreground: var(--rb-muted-text);
|
|
||||||
--color-border: var(--rb-outline);
|
|
||||||
--color-input: var(--rb-input);
|
|
||||||
--color-ring: var(--rb-ring);
|
|
||||||
|
|
||||||
--color-chart-1: var(--chart-1);
|
|
||||||
--color-chart-2: var(--chart-2);
|
|
||||||
--color-chart-3: var(--chart-3);
|
|
||||||
--color-chart-4: var(--chart-4);
|
|
||||||
--color-chart-5: var(--chart-5);
|
|
||||||
|
|
||||||
--color-sidebar: var(--sidebar);
|
|
||||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
||||||
--color-sidebar-primary: var(--sidebar-primary);
|
|
||||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
||||||
--color-sidebar-accent: var(--sidebar-accent);
|
|
||||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
||||||
--color-sidebar-border: var(--sidebar-border);
|
|
||||||
--color-sidebar-ring: var(--sidebar-ring);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
* {
|
|
||||||
@apply border-border outline-ring/50;
|
|
||||||
}
|
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+21
@@ -262,6 +262,9 @@ importers:
|
|||||||
'@storybook/test-runner':
|
'@storybook/test-runner':
|
||||||
specifier: ^0.19.1
|
specifier: ^0.19.1
|
||||||
version: 0.19.1(@swc/helpers@0.5.18)(@types/node@25.0.10)(storybook@8.6.15(prettier@3.5.3))(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.0.10)(typescript@5.8.3))
|
version: 0.19.1(@swc/helpers@0.5.18)(@types/node@25.0.10)(storybook@8.6.15(prettier@3.5.3))(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.0.10)(typescript@5.8.3))
|
||||||
|
'@tailwindcss/typography':
|
||||||
|
specifier: ^0.5.19
|
||||||
|
version: 0.5.19
|
||||||
'@tailwindcss/vite':
|
'@tailwindcss/vite':
|
||||||
specifier: ^4.1.4
|
specifier: ^4.1.4
|
||||||
version: 4.1.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2))
|
version: 4.1.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2))
|
||||||
@@ -3084,6 +3087,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==}
|
resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
|
|
||||||
|
'@tailwindcss/typography@0.5.19':
|
||||||
|
resolution: {integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==}
|
||||||
|
peerDependencies:
|
||||||
|
tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1'
|
||||||
|
|
||||||
'@tailwindcss/vite@4.1.18':
|
'@tailwindcss/vite@4.1.18':
|
||||||
resolution: {integrity: sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==}
|
resolution: {integrity: sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -6305,6 +6313,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
|
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
postcss-selector-parser@6.0.10:
|
||||||
|
resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
postcss-selector-parser@7.1.1:
|
postcss-selector-parser@7.1.1:
|
||||||
resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==}
|
resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -10922,6 +10934,10 @@ snapshots:
|
|||||||
'@tailwindcss/oxide-win32-arm64-msvc': 4.1.18
|
'@tailwindcss/oxide-win32-arm64-msvc': 4.1.18
|
||||||
'@tailwindcss/oxide-win32-x64-msvc': 4.1.18
|
'@tailwindcss/oxide-win32-x64-msvc': 4.1.18
|
||||||
|
|
||||||
|
'@tailwindcss/typography@0.5.19':
|
||||||
|
dependencies:
|
||||||
|
postcss-selector-parser: 6.0.10
|
||||||
|
|
||||||
'@tailwindcss/vite@4.1.18(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2))':
|
'@tailwindcss/vite@4.1.18(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tailwindcss/node': 4.1.18
|
'@tailwindcss/node': 4.1.18
|
||||||
@@ -15416,6 +15432,11 @@ snapshots:
|
|||||||
|
|
||||||
possible-typed-array-names@1.1.0: {}
|
possible-typed-array-names@1.1.0: {}
|
||||||
|
|
||||||
|
postcss-selector-parser@6.0.10:
|
||||||
|
dependencies:
|
||||||
|
cssesc: 3.0.0
|
||||||
|
util-deprecate: 1.0.2
|
||||||
|
|
||||||
postcss-selector-parser@7.1.1:
|
postcss-selector-parser@7.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
cssesc: 3.0.0
|
cssesc: 3.0.0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { cn } from '@ermnvldmr/stl';
|
import { cn } from '@ermnvldmr/stl';
|
||||||
import { VStack, HStack } from '@ermnvldmr/ui';
|
import { VStack, HStack, Markdown } from '@ermnvldmr/ui';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,7 +100,9 @@ export const ArticleLayout: React.FC<ArticleLayoutProps> = ({
|
|||||||
</VStack>
|
</VStack>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<section className="article-content">{children}</section>
|
<section className="article-content">
|
||||||
|
<Markdown>{children}</Markdown>
|
||||||
|
</section>
|
||||||
</article>
|
</article>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
Reference in new issue
Block a user