diff --git a/packages/ui/src/Colors.mdx b/packages/ui/src/Colors.mdx
new file mode 100644
index 0000000000..316f688d59
--- /dev/null
+++ b/packages/ui/src/Colors.mdx
@@ -0,0 +1,10 @@
+import { Meta, Canvas } from '@storybook/blocks';
+import * as ColorsStories from './Colors.stories';
+
+
+
+# Colors
+
+Below are the colors we use at Penumbra. We deliberately do not include RGB/hex values for our colors, as they should be used via tokens — e.g., `text.primary`, `neutral.light`, `destructive.main`, etc. — to ensure consistency.
+
+
diff --git a/packages/ui/src/Colors.stories.tsx b/packages/ui/src/Colors.stories.tsx
new file mode 100644
index 0000000000..6c196f59ec
--- /dev/null
+++ b/packages/ui/src/Colors.stories.tsx
@@ -0,0 +1,108 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { Grid } from './Grid';
+import { Technical } from './Typography';
+import styled from 'styled-components';
+import {
+ type ColorVariant,
+ type Color as TColor,
+ type TextColorVariant,
+} from './ThemeProvider/theme';
+import { Fragment } from 'react';
+import { media } from './utils/media';
+
+const meta: Meta = {};
+export default meta;
+
+const Label = styled.div`
+ display: flex;
+ height: 100%;
+
+ ${media.tablet`
+ align-items: center;
+ `}
+`;
+
+const Variants = styled.div`
+ display: grid;
+ gap: ${props => props.theme.spacing(4)};
+ grid-template-columns: 1fr;
+
+ ${media.tablet`
+ grid-template-columns: repeat(4, 1fr);
+ `}
+`;
+
+type VariantProps =
+ | {
+ $color: 'text';
+ $colorVariant: TextColorVariant;
+ }
+ | {
+ $color: Exclude;
+ $colorVariant: ColorVariant;
+ };
+
+const Variant = styled.div`
+ background-color: ${props =>
+ props.$color === 'text' ? 'transparent' : props.theme.color[props.$color][props.$colorVariant]};
+ border-radius: ${props => props.theme.borderRadius.xl};
+ color: ${props =>
+ props.$color === 'text'
+ ? props.theme.color.text[props.$colorVariant]
+ : props.$colorVariant === 'contrast' || props.$colorVariant === 'light'
+ ? props.theme.color[props.$color].dark
+ : props.theme.color.text.primary};
+ padding: ${props => props.theme.spacing(2)};
+`;
+
+const BASE_COLORS: Exclude[] = [
+ 'neutral',
+ 'primary',
+ 'secondary',
+ 'unshield',
+ 'destructive',
+ 'caution',
+ 'success',
+];
+
+const Color = >({ color }: { color: T }) => (
+
+
+
+
+
+
+ {color === 'text'
+ ? (['primary', 'secondary', 'disabled', 'special'] as const).map(variant => (
+
+ {variant}
+
+ ))
+ : (['main', 'light', 'dark', 'contrast'] as const).map(variant => (
+
+ {variant}
+
+ ))}
+
+
+
+);
+
+export const ColorGrid: StoryObj = {
+ tags: ['!dev'],
+ render: function Render() {
+ return (
+ <>
+
+
+
+ {BASE_COLORS.map(color => (
+
+ ))}
+
+ >
+ );
+ },
+};
diff --git a/packages/ui/src/Grid/index.stories.tsx b/packages/ui/src/Grid/index.stories.tsx
new file mode 100644
index 0000000000..14f95a435b
--- /dev/null
+++ b/packages/ui/src/Grid/index.stories.tsx
@@ -0,0 +1,74 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { Grid } from '.';
+import styled from 'styled-components';
+import { Text } from '../Text';
+
+const meta: Meta = {
+ component: Grid,
+ title: 'Grid',
+ tags: ['autodocs', '!dev'],
+ argTypes: {
+ container: { control: false },
+ mobile: { control: false },
+ tablet: { control: false },
+ desktop: { control: false },
+ lg: { control: false },
+ xl: { control: false },
+ as: { control: false },
+ },
+};
+export default meta;
+
+type Story = StoryObj;
+
+const Item = styled.div`
+ background-color: ${props => props.theme.color.neutral.main};
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: ${props => props.theme.spacing(2)};
+`;
+
+export const Demo: Story = {
+ render: function Render() {
+ return (
+
+
+ -
+ mobile=12
+
+
+
+ {Array(2)
+ .fill(null)
+ .map((_, index) => (
+
+ -
+ mobile=12 tablet=6
+
+
+ ))}
+
+ {Array(4)
+ .fill(null)
+ .map((_, index) => (
+
+ -
+ mobile=6 tablet=6 desktop=3
+
+
+ ))}
+
+ {Array(48)
+ .fill(null)
+ .map((_, index) => (
+
+ -
+ lg=1
+
+
+ ))}
+
+ );
+ },
+};
diff --git a/packages/ui/src/Grid/index.tsx b/packages/ui/src/Grid/index.tsx
new file mode 100644
index 0000000000..4d8c882456
--- /dev/null
+++ b/packages/ui/src/Grid/index.tsx
@@ -0,0 +1,135 @@
+import { PropsWithChildren } from 'react';
+import styled from 'styled-components';
+import { AsTransientProps, asTransientProps } from '../utils/asTransientProps';
+import { media } from '../utils/media';
+
+type GridElement = 'div' | 'main' | 'section';
+
+interface BaseGridProps extends Record {
+ /** Which element to use. Defaults to `'div'`. */
+ as?: GridElement;
+}
+
+interface GridContainerProps extends BaseGridProps {
+ /** Whether this is a grid container, vs. an item. */
+ container: true;
+
+ // For some reason, Storybook needs these properties to be defined on the
+ // container props interface in order to show their typings properly.
+ mobile?: undefined;
+ tablet?: undefined;
+ desktop?: undefined;
+ lg?: undefined;
+ xl?: undefined;
+}
+
+interface GridItemProps extends BaseGridProps {
+ /** Whether this is a grid container, vs. an item. */
+ container?: false;
+ /**
+ * The number of columns this grid item should span on mobile.
+ *
+ * The mobile grid layout can only be split in half, so you can only set a
+ * grid item to 6 or 12 columns on mobile.
+ */
+ mobile?: 6 | 12;
+ /**
+ * The number of columns this grid item should span on tablet.
+ *
+ * The tablet grid layout can only be split into six columns.
+ */
+ tablet?: 2 | 4 | 6 | 8 | 10 | 12;
+ /** The number of columns this grid item should span on desktop. */
+ desktop?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
+ /** The number of columns this grid item should span on large screens. */
+ lg?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
+ /** The number of columns this grid item should span on XL screens. */
+ xl?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
+}
+
+export type GridProps = PropsWithChildren;
+
+const Container = styled.div`
+ display: grid;
+ grid-template-columns: repeat(12, 1fr);
+ gap: ${props => props.theme.spacing(4)};
+`;
+
+const Item = styled.div>>`
+ ${props => media.mobile`
+ grid-column: span ${props.$mobile ?? 12};
+ `}
+
+ ${props =>
+ props.$tablet &&
+ media.tablet`
+ grid-column: span ${props.$tablet};
+ `}
+
+ ${props =>
+ props.$desktop &&
+ media.desktop`
+ grid-column: span ${props.$desktop};
+ `}
+
+ ${props =>
+ props.$lg &&
+ media.lg`
+ grid-column: span ${props.$lg};
+ `}
+
+ ${props =>
+ props.$xl &&
+ media.xl`
+ grid-column: span ${props.$xl};
+ `}
+`;
+
+/**
+ * A responsive grid component that makes 12-column layouts super easy to build.
+ *
+ * Pass the `container` prop to the root `` component; then, any nested
+ * children ``s will be treated as grid items. You can customize which
+ * HTML element to use for each grid container or item by passing the element's
+ * name via the optional `as` prop.
+ *
+ * Use the `` component — rather than styling your own HTML elements
+ * with `display: grid` — to ensure consistent behavior (such as grid gutters)
+ * throughout your app.
+ *
+ * ```tsx
+ *
+ * This will span the full width on all screen sizes.
+ *
+ * So will this.
+ *
+ *
+ * These will span the full width on mobile...
+ *
+ *
+ *
+ * ...but half the width on desktop.
+ *
+ *
+ *
+ * These will...
+ *
+ *
+ *
+ * ...take up...
+ *
+ *
+ *
+ * ...one third each.
+ *
+ *
+ * ```
+ */
+export const Grid = ({ container, children, as = 'div', ...props }: GridProps) =>
+ container ? (
+ {children}
+ ) : (
+ -
+ {children}
+
+ );
diff --git a/packages/ui/src/Icon/index.stories.ts b/packages/ui/src/Icon/index.stories.ts
index f8f114b316..4e15331937 100644
--- a/packages/ui/src/Icon/index.stories.ts
+++ b/packages/ui/src/Icon/index.stories.ts
@@ -5,7 +5,7 @@ import { Icon } from '.';
const meta: Meta = {
component: Icon,
- tags: ['autodocs'],
+ tags: ['autodocs', '!dev'],
argTypes: {
IconComponent: {
options: ['ArrowRightLeft', 'Send', 'Wallet'],
diff --git a/packages/ui/src/Icon/index.tsx b/packages/ui/src/Icon/index.tsx
index eebab87627..154f2d9969 100644
--- a/packages/ui/src/Icon/index.tsx
+++ b/packages/ui/src/Icon/index.tsx
@@ -46,6 +46,10 @@ const PROPS_BY_SIZE: Record> = {
* component rather than rendering Lucide icon components directly, since this
* component standardizes the stroke width and sizes throughout the Penumbra
* ecosystem.
+ *
+ * ```tsx
+ *
+ * ```
*/
export const Icon = ({ IconComponent, size = 'sm', color }: IconProps) => (
diff --git a/packages/ui/src/Text/index.stories.tsx b/packages/ui/src/Text/index.stories.tsx
new file mode 100644
index 0000000000..2fccaf195f
--- /dev/null
+++ b/packages/ui/src/Text/index.stories.tsx
@@ -0,0 +1,155 @@
+import type { Meta, StoryObj } from '@storybook/react';
+
+import { Text } from '.';
+import styled from 'styled-components';
+import { useArgs } from '@storybook/preview-api';
+
+const meta: Meta = {
+ component: Text,
+ tags: ['autodocs', '!dev'],
+ argTypes: {
+ h1: { control: false },
+ h2: { control: false },
+ h3: { control: false },
+ h4: { control: false },
+ large: { control: false },
+ body: { control: false },
+ p: { control: false },
+ strong: { control: false },
+ detail: { control: false },
+ small: { control: false },
+ technical: { control: false },
+
+ as: {
+ options: ['span', 'div', 'h1', 'h2', 'h3', 'h4', 'p', 'main', 'section'],
+ },
+ },
+};
+export default meta;
+
+const Wrapper = styled.div<{ $dir: 'column' | 'row' }>`
+ display: flex;
+ flex-direction: ${props => props.$dir};
+ ${props => (props.$dir === 'row' ? `align-items: center;` : '')}
+ gap: ${props => props.theme.spacing(2)};
+`;
+
+const OPTIONS = [
+ 'h1',
+ 'h2',
+ 'h3',
+ 'h4',
+ 'large',
+ 'body',
+ 'p',
+ 'strong',
+ 'detail',
+ 'small',
+ 'technical',
+] as const;
+
+const Option = ({
+ value,
+ checked,
+ onSelect,
+}: {
+ value: (typeof OPTIONS)[number];
+ checked: boolean;
+ onSelect: (value: (typeof OPTIONS)[number]) => void;
+}) => (
+
+);
+export const KitchenSink: StoryObj = {
+ args: {
+ children: 'The quick brown fox jumps over the lazy dog.',
+ h1: true,
+ as: 'span',
+ },
+
+ render: function Render(props) {
+ const [, updateArgs] = useArgs();
+
+ const onSelect = (option: (typeof OPTIONS)[number]) =>
+ updateArgs(
+ OPTIONS.reduce(
+ (prev, curr) => ({
+ ...prev,
+ [curr]: curr === option ? true : undefined,
+ }),
+ {},
+ ),
+ );
+
+ return (
+
+
+ Text style:
+ {OPTIONS.map(option => (
+
+ ))}
+
+
+
+
+ );
+ },
+};
+
+export const UsageExample: StoryObj = {
+ render: function Render() {
+ return (
+ <>
+ h1. Typography
+ h2. This is a section
+
+ Here is some filler text: Giggster kickstarter painting with light
+ academy award charlie kaufman shotdeck breakdown services indie white balance. Student
+ emmys sound design ots character arc low angle coming-of-age composition. Storyboard beat
+ sheet greenlight cowboy shot margarita shot blocking foley stage seed&spark.
+
+
+
+ Shot list low angle mit out sound telephoto rec.709 high angle eyeline assembly cut 8 1/2
+ dga. Post-viz circle of confusion location scout unpaid internship reality of doing genre
+ film. Jean-luc godard ilm symbolism alexa mini white balance margarita shot. Jordan peele
+ log line ryan coogler actors access.
+
+
+ h2. Section two
+
+ Silent film conflict sound design blocking script treatment. Teal and orange composition
+ fotokem third act blackmagic ingmar bergman jordan peele rembrandt lighting critical
+ darling silent film. Wes anderson arthouse diegetic sound after effects.
+
+
+ This is some large text.
+
+
+ White balance crafty debut film pan up 180-degree rule academy award exposure triangle
+ director's vision. Lavs led wall the actor prepares wrylies character arc stinger
+ sanford meisner. Given circumstances under-exposed jordan peele color grade nomadland team
+ deakins crafty dogme 95. French new wave pan up save the cat contrast ratio blue filter
+ cinema studies super 16 jump cut cannes unreal engine.
+
+
+
+ Establishing shot stella adler ludwig göransson first-time director shotdeck fotokem
+ over-exposed flashback reality of doing color grade. Fetch coffee student emmys indie key
+ light rembrandt lighting. Undercranking beat beat scriptnotes podcast. Sound design
+ academy award day-for-night christopher nolan undercranking. Unreal engine visionary match
+ cut grain vs. noise 35mm anti-hero production design.
+
+ >
+ );
+ },
+};
diff --git a/packages/ui/src/Text/index.tsx b/packages/ui/src/Text/index.tsx
new file mode 100644
index 0000000000..3b3ca6d3cc
--- /dev/null
+++ b/packages/ui/src/Text/index.tsx
@@ -0,0 +1,242 @@
+import styled, { WebTarget } from 'styled-components';
+import { body, detail, h1, h2, h3, h4, large, small, strong, technical } from '../utils/typography';
+import { ReactNode } from 'react';
+
+const H1 = styled.h1`
+ ${h1}
+`;
+
+const H2 = styled.h2`
+ ${h2}
+`;
+
+const H3 = styled.h3`
+ ${h3}
+`;
+
+const H4 = styled.h4`
+ ${h4}
+`;
+
+const Large = styled.span`
+ ${large}
+`;
+
+const Body = styled.span`
+ ${body}
+`;
+
+const Strong = styled.span`
+ ${strong}
+`;
+
+const Detail = styled.span`
+ ${detail}
+`;
+
+const Small = styled.span`
+ ${small}
+`;
+
+const Technical = styled.span`
+ ${technical}
+`;
+
+const P = styled.p`
+ ${body}
+
+ margin-bottom: ${props => props.theme.lineHeight.textBase};
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+`;
+
+/**
+ * Utility interface to be used below to ensure that only one text type is used
+ * at a time.
+ */
+interface NeverTextTypes {
+ h1?: never;
+ h2?: never;
+ h3?: never;
+ h4?: never;
+ large?: never;
+ p?: never;
+ strong?: never;
+ detail?: never;
+ small?: never;
+ technical?: never;
+ body?: never;
+}
+
+type TextType =
+ | (Omit & {
+ /**
+ * Renders a styled ``. Pass the `as` prop to use a different HTML
+ * element with the same styling.
+ */
+ h1: true;
+ })
+ | (Omit & {
+ /**
+ * Renders a styled ``. Pass the `as` prop to use a different HTML
+ * element with the same styling.
+ */
+ h2: true;
+ })
+ | (Omit & {
+ /**
+ * Renders a styled ``. Pass the `as` prop to use a different HTML
+ * element with the same styling.
+ */
+ h3: true;
+ })
+ | (Omit & {
+ /**
+ * Renders a styled ``. Pass the `as` prop to use a different HTML
+ * element with the same styling.
+ */
+ h4: true;
+ })
+ | (Omit & {
+ /**
+ * Renders bigger text used for section titles. Renders a `` by
+ * default; pass the `as` prop to use a different HTML element with the
+ * same styling.
+ */
+ large: true;
+ })
+ | (Omit & {
+ /**
+ * Renders a styled `` tag with a bottom-margin (unless it's the last
+ * child). Aside from the margin, `` is identical to ``.
+ *
+ * Note that this is the only component in the entire Penumbra UI library
+ * that renders an external margin. It's a convenience for developers who
+ * don't want to wrap each `` in a `` with the
+ * appropriate margin, or a flex columnn with a gap.
+ */
+ p: true;
+ })
+ | (Omit & {
+ /**
+ * Emphasized body text.
+ *
+ * Renders a `` by default; pass the `as` prop to use a different
+ * HTML element with the same styling.
+ */
+ strong: true;
+ })
+ | (Omit & {
+ /**
+ * Detail text used for small bits of tertiary information.
+ *
+ * Renders a `` by default; pass the `as` prop to use a different
+ * HTML element with the same styling.
+ */
+ detail: true;
+ })
+ | (Omit & {
+ /**
+ * Small text used for secondary information.
+ *
+ * Renders a `` by default; pass the `as` prop to use a different
+ * HTML element with the same styling.
+ */
+ small: true;
+ })
+ | (Omit & {
+ /**
+ * Monospaced text used for code, values, and other technical information.
+ *
+ * Renders a `` by default; pass the `as` prop to use a different
+ * HTML element with the same styling.
+ */
+ technical: true;
+ })
+ | (Omit & {
+ /**
+ * Body text used throughout most of our UIs.
+ *
+ * Renders a `` by default; pass the `as` prop to use a different
+ * HTML element with the same styling.
+ */
+ body?: true;
+ });
+
+export type TextProps = TextType & {
+ children?: ReactNode;
+ /**
+ * Which component or HTML element to render this text as.
+ *
+ * @example
+ * ```tsx
+ * This is a span with H1 styling
+ * ```
+ */
+ as?: WebTarget;
+};
+
+/**
+ * All-purpose text wrapper for quickly styling text per the Penumbra UI
+ * guidelines.
+ *
+ * Use with a _single_ text style name:
+ *
+ * ```tsx
+ * This will be rendered with the `h1` style.
+ * This will be rendered with the `body` style.
+ *
+ * INCORRECT: This will result in a TypeScript error. Only use one text style
+ * at a time.
+ *
+ * ```
+ *
+ * When no text style is passed, it will render using the `body` style.
+ *
+ * The heading text styles are rendered as their corresponding heading tags
+ * (``, ``, etc.), and the `p` style is rendered as a `` tag.
+ * All other styles are rendered as ``s. To customize which tag is
+ * rendered without affecting its appearance, use the `as` prop:
+ *
+ * ```tsx
+ *
+ * This will render with the h1 style, but inside an inline span tag.
+ *
+ * ```
+ */
+export const Text = (props: TextProps) => {
+ if (props.h1) {
+ return ;
+ }
+ if (props.h2) {
+ return ;
+ }
+ if (props.h3) {
+ return ;
+ }
+ if (props.h4) {
+ return ;
+ }
+ if (props.large) {
+ return ;
+ }
+ if (props.strong) {
+ return ;
+ }
+ if (props.detail) {
+ return ;
+ }
+ if (props.small) {
+ return ;
+ }
+ if (props.technical) {
+ return ;
+ }
+ if (props.p) {
+ return ;
+ }
+
+ return ;
+};
diff --git a/packages/ui/src/utils/asTransientProps.test.ts b/packages/ui/src/utils/asTransientProps.test.ts
new file mode 100644
index 0000000000..1245812c13
--- /dev/null
+++ b/packages/ui/src/utils/asTransientProps.test.ts
@@ -0,0 +1,18 @@
+import { describe, expect, it } from 'vitest';
+import { asTransientProps } from './asTransientProps';
+
+describe('asTransientProps()', () => {
+ it('converts all properties to have a `$` prefix', () => {
+ const props = {
+ size: 'lg',
+ color: 'red',
+ };
+
+ const expected = {
+ $size: 'lg',
+ $color: 'red',
+ };
+
+ expect(asTransientProps(props)).toEqual(expected);
+ });
+});
diff --git a/packages/ui/src/utils/asTransientProps.ts b/packages/ui/src/utils/asTransientProps.ts
new file mode 100644
index 0000000000..12b7e14e03
--- /dev/null
+++ b/packages/ui/src/utils/asTransientProps.ts
@@ -0,0 +1,25 @@
+// Thanks to https://stackoverflow.com/a/65278278/974981 for the prefixer
+// utility types
+type Prefix = `${K}${T}`;
+
+type Prefixer = {
+ [P in keyof K as Prefix]: K[P];
+};
+
+export type AsTransientProps> = Prefixer;
+
+/**
+ * Converts a props object to an object of "transient props" -- i.e., where each
+ * key is prefixed with a `$` to indicate to styled-components that it should
+ * not be passed down to the DOM element.
+ *
+ * @see https://styled-components.com/docs/api#transient-props
+ * @internal
+ */
+export const asTransientProps = >(
+ props: T,
+): AsTransientProps =>
+ Object.entries(props).reduce>>((prev, curr) => {
+ const [key, value] = curr;
+ return { ...prev, [`$${key}`]: value };
+ }, {}) as unknown as AsTransientProps;
diff --git a/packages/ui/src/utils/typography.ts b/packages/ui/src/utils/typography.ts
new file mode 100644
index 0000000000..c39433f618
--- /dev/null
+++ b/packages/ui/src/utils/typography.ts
@@ -0,0 +1,126 @@
+import { css } from 'styled-components';
+
+/**
+ * This file contains styles that are used throughout the Penumbra UI library.
+ * Many of them correlate 1-to-1 to specific components (such as `h1`, `large`,
+ * etc.), while others are base styles shared by a number of components.
+ */
+
+const base = `
+ margin: 0;
+`;
+
+export const h1 = css`
+ ${base}
+
+ font-family: ${props => props.theme.font.heading};
+ font-size: ${props => props.theme.fontSize.text6xl};
+ font-weight: 500;
+ line-height: ${props => props.theme.lineHeight.text6xl};
+`;
+
+export const h2 = css`
+ ${base}
+
+ font-family: ${props => props.theme.font.heading};
+ font-size: ${props => props.theme.fontSize.text5xl};
+ font-weight: 500;
+ line-height: ${props => props.theme.lineHeight.text5xl};
+`;
+
+export const h3 = css`
+ ${base}
+
+ font-family: ${props => props.theme.font.heading};
+ font-size: ${props => props.theme.fontSize.text4xl};
+ font-weight: 500;
+ line-height: ${props => props.theme.lineHeight.text4xl};
+`;
+
+export const h4 = css`
+ ${base}
+
+ font-family: ${props => props.theme.font.heading};
+ font-size: ${props => props.theme.fontSize.text3xl};
+ font-weight: 500;
+ line-height: ${props => props.theme.lineHeight.text3xl};
+`;
+
+export const large = css`
+ ${base}
+
+ font-family: ${props => props.theme.font.default};
+ font-size: ${props => props.theme.fontSize.textLg};
+ font-weight: 500;
+ line-height: ${props => props.theme.lineHeight.textLg};
+`;
+
+export const body = css`
+ ${base}
+
+ font-family: ${props => props.theme.font.default};
+ font-size: ${props => props.theme.fontSize.textBase};
+ font-weight: 400;
+ line-height: ${props => props.theme.lineHeight.textBase};
+`;
+
+export const strong = css`
+ ${base}
+
+ font-family: ${props => props.theme.font.default};
+ font-size: ${props => props.theme.fontSize.textBase};
+ font-weight: 500;
+ line-height: ${props => props.theme.lineHeight.textBase};
+`;
+
+export const detail = css`
+ ${base}
+
+ font-family: ${props => props.theme.font.default};
+ font-size: ${props => props.theme.fontSize.textXs};
+ font-weight: 500;
+ line-height: ${props => props.theme.lineHeight.textXs};
+`;
+
+export const small = css`
+ ${base}
+
+ font-family: ${props => props.theme.font.default};
+ font-size: ${props => props.theme.fontSize.textSm};
+ font-weight: 400;
+ line-height: ${props => props.theme.lineHeight.textSm};
+`;
+
+export const tableItem = css`
+ ${base}
+
+ font-family: ${props => props.theme.font.default};
+ font-size: ${props => props.theme.fontSize.textBase};
+ font-weight: 400;
+ line-height: ${props => props.theme.lineHeight.textBase};
+`;
+
+export const tableHeading = css`
+ ${base}
+
+ font-family: ${props => props.theme.font.default};
+ font-size: ${props => props.theme.fontSize.textBase};
+ font-weight: 500;
+ line-height: ${props => props.theme.lineHeight.textBase};
+`;
+
+export const technical = css`
+ ${base}
+
+ font-family: ${props => props.theme.font.mono};
+ font-size: ${props => props.theme.fontSize.textSm};
+ font-weight: 500;
+ line-height: ${props => props.theme.lineHeight.textSm};
+`;
+
+export const button = css`
+ font-family: ${props => props.theme.font.default};
+ font-size: ${props => props.theme.fontSize.textBase};
+ font-weight: 500;
+ line-height: ${props => props.theme.lineHeight.textBase};
+`;