X*2SR&CMj1w
z+VK^%-<_Yre&hT|##o!zVLs&xA`_v{Xooo8KyUPUXouW>4xm4E7<8&der8{}wgfnc
zg|W@lKZ$HvuS-@(_C5Q_gnwY8ze^ja%jbEjO0{{(?80*SkF@7ElgL9R<->*?PTI3v
zK2OZV@DhAr|Hhbs{X-v4KhJl{(I2|~NbCLsUjK8nwmAC!*_5_t*
zhwn_%wz7P-o4CDx74t02X>lIu6L+cH1J~SoW4dzfr(NPWiAx*sKaX2LW4$fDP2@M=
zPx=OW+XVSwAF(|_?MMF(%KH*--j6!X`z<8SeR*_xVXo?#dzHRp*+J#we!<#A`qDx3
z$T?R)`(HujgAdO0u6-e9VqlxK&!XwVH4Tij{Ns@2=nwzrQr_2Cd8A%p8I)bF_ojZH
zGe+e!c%3?mF)nxV^JvI?Dg6&$V+6+mz2)n3_Mx^flvn;Ie@!}1t=VQ?X8c)@2_>IJ
z?F(Y?bFM@A>|@$TV$r*{m*g+?6My3p;&OuGab`>q38A
zN=*13;b`XZ`CT%MA3j7I4WDmi>KJ?dgtM(J7#n++m0W)&+7Ft7J|*pYal6^|DvImT
z+D8s*`;8oo^RLQI`R=$O(kI<-R~P?;eiA;#?2f6J_(>c$<##sd_hjHRg3peG?sdO0
zi?$EJ`EQ{EE(R|S1r7T6TgUH{HP__*8t6NWy-EXk6*(%^H~D^V^{yB!B;JK){R#nQ`*+CPtmr5Z;G<*c>fjlFL{z<@tn)gZynHXG6(M3e6)w}
zV;;*4Z~gR_ajhM`pNu{g-+_P}anm_Oo^viKWxLq>81wr~T|(X(4%zUop<%qm?}u??
zPLw=OKK0k%P%U4CkMT|&dH-C@U%y@Hkcl|gc{}P>`eDWDuZzpqVS_tpXQ&&l{ISSA
z<9E+@Bpuv!J5|Nscl#VXN*zBO&+r=`dZR68e)_83l#$}^*C*tMw4W)w%Q@oi=AaG8
zc`*OU?=Mi7&}J8FKk7e%{uOm1ze&K&Mjvp^wf-gx*DG<2O0oA*cVGIK)Jvo}{TKf>
z?NU{KvG+4^tA@Nsy+r=uv+Mt(%AvjInlRFycDWVx`1_A}u<-+xerdq{5$FRi|8fmB
z^TAW#t4ufr?d(^`zm&J88v9A;l{MHmqA$1A^|LLO0ouw)@YRxA6{1-eLN^~>C_kE)
zAMPs$!2kMqW`KOI4&St9ewe!Jnj@qZ_HzhIxM0#5a@EMHuJWtPh>j5jA__zlh$s+I
zAfiA-frtVT1tJPW6o@DgQ6Qo~M1hC`5d|U&L==c95K$naKtzFv0ucow3Pco$C=gK~
QqCiA}hyoD>_J0ce7gEZOiU0rr
literal 0
HcmV?d00001
diff --git a/packages/styleguide/jest.config.ts b/packages/styleguide/jest.config.ts
deleted file mode 100644
index 4bd8d3e350..0000000000
--- a/packages/styleguide/jest.config.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-/* eslint-disable */
-import base from '../../jest.config.base';
-
-export default base('styleguide', {});
diff --git a/packages/styleguide/package.json b/packages/styleguide/package.json
index cba74b9c73..3d0511f7ca 100644
--- a/packages/styleguide/package.json
+++ b/packages/styleguide/package.json
@@ -1,50 +1,9 @@
{
"name": "@codecademy/styleguide",
"description": "Styleguide & Component library for codecademy.com",
- "version": "68.2.7",
+ "version": "68.1.3",
"author": "Codecademy Engineering",
- "dependencies": {
- "@codecademy/gamut": "57.5.2",
- "@codecademy/gamut-icons": "9.34.1",
- "@codecademy/gamut-illustrations": "0.49.2",
- "@codecademy/gamut-patterns": "0.9.17",
- "@codecademy/gamut-styles": "17.1.2",
- "@codecademy/macros": "3.0.4",
- "@codecademy/variance": "0.21.4",
- "@codecademy/webpack-config": "6.4.0",
- "@emotion/cache": "^11.4.0",
- "@emotion/react": "^11.4.0",
- "@emotion/styled": "^11.3.0",
- "@storybook/addon-a11y": "6.3.4",
- "@storybook/addon-actions": "6.3.4",
- "@storybook/addon-controls": "6.3.4",
- "@storybook/addon-docs": "6.3.4",
- "@storybook/addon-essentials": "6.3.4",
- "@storybook/addon-links": "6.3.4",
- "@storybook/addons": "6.3.4",
- "@storybook/react": "6.3.4",
- "@storybook/theming": "6.3.4",
- "@testing-library/react": "^12.1.2",
- "@types/mdx-js__react": "^1.5.3",
- "autoprefixer": "^9.7.4",
- "babel-preset-codecademy": "6.0.1-alpha.042681.0",
- "css-loader": "^3.5.2",
- "focus-visible": "^5.2.0",
- "glob": "^7.1.6",
- "invariant": "2.2.4",
- "jsdom": "20.0.0",
- "lodash": "4.17.20",
- "polished": "^4.1.2",
- "postcss-loader": "^3.0.0",
- "react": "^17.0.2",
- "react-dom": "^17.0.2",
- "sass": "1.56.2",
- "sass-loader": "^10.1.1",
- "storybook-addon-designs": "^6.0.1",
- "style-loader": "^1.2.0",
- "typescript": "4.4.2",
- "webpack": "4.46.0"
- },
+ "dependencies": {},
"license": "MIT",
"publishConfig": {
"access": "public"
diff --git a/packages/styleguide/project.json b/packages/styleguide/project.json
index db12f19f6d..fc160c85da 100644
--- a/packages/styleguide/project.json
+++ b/packages/styleguide/project.json
@@ -1,28 +1,37 @@
{
"name": "styleguide",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
- "sourceRoot": "packages/styleguide/stories",
- "projectType": "application",
+ "sourceRoot": "packages/styleguide/src",
+ "projectType": "library",
+ "tags": [],
"targets": {
- "build-storybook": {
- "outputs": ["{projectRoot}/dist"],
- "executor": "nx:run-commands",
+ "lint": {
+ "executor": "@nx/eslint:lint"
+ },
+ "storybook": {
+ "executor": "@nx/storybook:storybook",
"options": {
- "cwd": "packages/styleguide",
- "commands": [
- "NODE_ENV=production ./node_modules/.bin/build-storybook --quiet -c .storybook -o ./dist"
- ],
- "parallel": false
+ "port": 6006,
+ "configDir": "packages/styleguide/.storybook"
+ },
+ "configurations": {
+ "ci": {
+ "quiet": true
+ }
}
},
- "test": {
- "executor": "@nx/jest:jest",
- "outputs": ["{workspaceRoot}/coverage/packages/styleguide"],
+ "build-storybook": {
+ "executor": "@nx/storybook:build",
+ "outputs": ["{options.outputDir}"],
"options": {
- "jestConfig": "packages/styleguide/jest.config.ts",
- "passWithNoTests": true
+ "outputDir": "dist/storybook/styleguide",
+ "configDir": "packages/styleguide/.storybook"
+ },
+ "configurations": {
+ "ci": {
+ "quiet": true
+ }
}
}
- },
- "tags": []
+ }
}
diff --git a/packages/styleguide/stories/About.stories.mdx b/packages/styleguide/src/lib/About.mdx
similarity index 69%
rename from packages/styleguide/stories/About.stories.mdx
rename to packages/styleguide/src/lib/About.mdx
index a7ac3c51eb..cf1c531f4b 100644
--- a/packages/styleguide/stories/About.stories.mdx
+++ b/packages/styleguide/src/lib/About.mdx
@@ -1,10 +1,16 @@
-import { Meta } from '@storybook/addon-docs/blocks';
+import { Meta } from '@storybook/blocks';
-import { TableOfContents } from '~styleguide/blocks';
+import { AboutHeader } from '~styleguide/blocks';
+
+export const parameters = {
+ id: 'Gamut',
+ title: 'Gamut',
+ subtitle: 'The design system for Codecademy ✨',
+};
-**The design system for Codecademy ✨**
+
Gamut components, styles, and utilities are the core of the Codecademy brand shared across its websites.
They're most notably used on [codecademy.com](https://codecademy.com) but are also found on our internal websites and tools, as well as on our mobile application.
@@ -12,5 +18,3 @@ They're most notably used on [codecademy.com](https://codecademy.com) but are al
This storybook represents components found in the Codecademy [Client Modules](https://github.com/Codecademy/gamut) repo.
Its organization roughly aligns with variations of [Atomic Design](https://bradfrost.com/blog/post/atomic-web-design/).
-
-
diff --git a/packages/styleguide/src/lib/Atoms/About.mdx b/packages/styleguide/src/lib/Atoms/About.mdx
new file mode 100644
index 0000000000..84efe435de
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/About.mdx
@@ -0,0 +1,139 @@
+import { Meta } from '@storybook/blocks';
+
+import { AboutHeader, TableOfContents } from '~styleguide/blocks';
+
+export const parameters = {
+ id: 'Atoms',
+ title: 'Atoms',
+ subtitle:
+ 'Atoms are our basic building block components. They are the most granular, low-level visuals in our system.',
+};
+
+
+
+
+
+> Atoms are the basic building blocks of matter. Applied to web interfaces, atoms are our HTML tags, such as a form label, an input or a button. Atoms can also include more abstract elements like color palettes, fonts and even more invisible aspects of an interface like animations.
+>
+> [- Brad Frost, author of "Atomic Design"](https://bradfrost.com/blog/post/atomic-web-design/#atoms)
+
+
+
+
diff --git a/packages/styleguide/src/lib/Atoms/Animation/Animation.mdx b/packages/styleguide/src/lib/Atoms/Animation/Animation.mdx
new file mode 100644
index 0000000000..89e878a218
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Animation/Animation.mdx
@@ -0,0 +1,29 @@
+import { Canvas, Controls, Meta } from '@storybook/blocks';
+
+import { ComponentHeader } from '~styleguide/blocks';
+
+import * as AnimationStories from './Animation.stories';
+
+export const parameters = {
+ subtitle: 'Controlled containers for predictable animations.',
+ status: 'current',
+ source: {
+ repo: 'gamut',
+ githubLink:
+ 'https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/Animation',
+ },
+};
+
+
+
+
+
+## Usage
+
+Animations should not be the click target of actions, but rather the container for the content you wish to animate.
+
+## Playground
+
+
+
+
diff --git a/packages/styleguide/src/lib/Atoms/Animation/Animation.stories.tsx b/packages/styleguide/src/lib/Atoms/Animation/Animation.stories.tsx
new file mode 100644
index 0000000000..8e05ac4d15
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Animation/Animation.stories.tsx
@@ -0,0 +1,23 @@
+import { Rotation, StrokeButton } from '@codecademy/gamut';
+import { MiniChevronDownIcon } from '@codecademy/gamut-icons';
+import type { Meta } from '@storybook/react';
+import { useState } from 'react';
+
+const meta: Meta = {
+ component: Rotation,
+ args: {},
+};
+
+export default meta;
+
+export const Default: React.FC = () => {
+ const [isRotated, setRotated] = useState(false);
+
+ return (
+ setRotated(!isRotated)}>
+
+
+
+
+ );
+};
diff --git a/packages/styleguide/src/lib/Atoms/Badge/Badge.mdx b/packages/styleguide/src/lib/Atoms/Badge/Badge.mdx
new file mode 100644
index 0000000000..8f52b8a487
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Badge/Badge.mdx
@@ -0,0 +1,119 @@
+import { Canvas, Controls, Meta } from '@storybook/blocks';
+
+import {
+ Callout,
+ ComponentHeader,
+ ImageWrapper,
+ LinkTo,
+} from '~styleguide/blocks';
+
+import * as BadgeStories from './Badge.stories';
+
+export const parameters = {
+ subtitle:
+ 'Badges are generally used as a standard way to highlight a short piece of text, likely a single word.',
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/file/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?type=design&node-id=29959-39721&mode=design&t=27y0ZBTRsAgBmZ9z-0',
+ },
+ status: 'current',
+ source: {
+ repo: 'gamut',
+ githubLink:
+ 'https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/Badge',
+ },
+};
+
+
+
+
+
+## Usage
+
+Use `Badge`s to display read-only information like statuses, attributes, and other empathsized information.
+
+### Best practices:
+
+- Use badges sparingly, as too many can dilute their effectiveness.
+
+### When NOT to use:
+
+- **Interactivity**- when text is meant to be clickable, use the Button or Anchor (link) components instead.
+- **Categorization**- for labeling or catgorizing content, use the Tag component instead.
+
+## Anatomy
+
+
+
+1. **Leading icon** _(optional)_
+
+- Use to reinforce the badge's message and improve scannability
+
+2. **Badge label**
+
+- Limit 1-2 words
+- Display read-only information like statuses, attributes, and other emphasized information
+
+## Variants
+
+### Primary
+
+Use the primary variant to highlight important information or statuses that need to be noticed.
+
+
+
+### Secondary
+
+Use the secondary variant to display supporting information or less critical statuses.
+
+
+
+### Tertiary
+
+Use the tertiary variant to indicate background or supplementary information with minimal emphasis.
+
+
+
+### Tertiary-fill
+
+Use the tertiary-fill variant when a tertiary badge needs to sit above other content. This variant includes a bg-color that can overlap color modes and unpredictable content.
+
+
+
+### Accent
+
+Use the accent variant to draw attention to important details or statuses with a noticeable but less dominant emphasis than the primary badge.
+
+
+
+
+
+## Sizes
+We have two `size` options, which should be used according to their context.
+
+### Default size
+Use `primary` when the `Badge` is a standalone entitity.
+
+
+
+### Small size
+Use `sm` when the `Badge` is inline with other components such as Text.
+
+
+## Leading icon
+Include the `icon` property when the icon reinforces the badge’s message and improves scannability. Since badges are inherently small, it's recommended to use Gamut's mini icons. Below are examples of using icons with the different `Badge` sizes.
+
+### Default size with icon
+
+
+### Small size with icon
+
+
+## Playground
+
+
+
+
diff --git a/packages/styleguide/src/lib/Atoms/Badge/Badge.stories.tsx b/packages/styleguide/src/lib/Atoms/Badge/Badge.stories.tsx
new file mode 100644
index 0000000000..87ef4f5a1d
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Badge/Badge.stories.tsx
@@ -0,0 +1,72 @@
+import { Badge } from '@codecademy/gamut';
+import { MiniStarIcon, MiniWarningTriangleIcon } from '@codecademy/gamut-icons';
+import type { Meta, StoryObj } from '@storybook/react';
+
+import { TertiaryFillExample } from './examples';
+
+const meta: Meta = {
+ component: Badge,
+ args: { children: 'Badge' },
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {},
+};
+
+export const Secondary: Story = {
+ args: {
+ variant: 'secondary',
+ },
+};
+
+export const Tertiary: Story = {
+ args: {
+ variant: 'tertiary',
+ },
+};
+
+export const TertiaryFill: Story = {
+ render: () => {
+ return ;
+ },
+};
+
+export const Accent: Story = {
+ args: {
+ variant: 'accent',
+ },
+};
+
+export const DefaultSize: Story = {
+ args: {
+ children: 'default size'
+ },
+}
+
+export const SmallSize: Story = {
+ args: {
+ children: 'sm size',
+ size: 'sm',
+ variant: 'tertiary'
+ },
+}
+
+export const DefaultSizeWithIcon: Story = {
+ args: {
+ children: 'sample icon',
+ variant: 'tertiaryFill',
+ icon: MiniStarIcon,
+ }
+}
+
+export const SmallSizeWithIcon: Story = {
+ args: {
+ children: 'sm icon',
+ size: 'sm',
+ variant: 'accent',
+ icon: MiniWarningTriangleIcon,
+ }
+}
diff --git a/packages/styleguide/src/lib/Atoms/Badge/examples.tsx b/packages/styleguide/src/lib/Atoms/Badge/examples.tsx
new file mode 100644
index 0000000000..7a4eea1ad6
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Badge/examples.tsx
@@ -0,0 +1,23 @@
+import { Badge, BadgeProps, Box } from '@codecademy/gamut';
+import { MiniStarIcon } from '@codecademy/gamut-icons';
+import { css } from '@codecademy/gamut-styles';
+import styled from '@emotion/styled';
+
+export const BadgeTemplate: React.FC = (args) => (
+ {args.children ? args.children : args.variant}
+);
+
+const AbsoluteBadge = styled(Badge)(
+ css({ position: 'absolute', top: 12, left: 12 })
+);
+
+export const TertiaryFillExample = () => {
+ return (
+
+
+ Badge
+
+
+
+ );
+};
diff --git a/packages/styleguide/src/lib/Atoms/Buttons/About.mdx b/packages/styleguide/src/lib/Atoms/Buttons/About.mdx
new file mode 100644
index 0000000000..ed70293046
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Buttons/About.mdx
@@ -0,0 +1,58 @@
+import { Meta } from '@storybook/blocks';
+
+import { AboutHeader, TableOfContents } from '~styleguide/blocks';
+
+export const parameters = {
+ id: 'Atoms/Buttons',
+ title: 'Buttons',
+ subtitle:
+ 'Buttons that provide an element for interaction on a page.',
+};
+
+
+
+
+
+
+
+
+
diff --git a/packages/styleguide/src/lib/Atoms/Buttons/Button/Button.mdx b/packages/styleguide/src/lib/Atoms/Buttons/Button/Button.mdx
new file mode 100644
index 0000000000..7c037e3c1b
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Buttons/Button/Button.mdx
@@ -0,0 +1,74 @@
+import { Canvas, Meta } from '@storybook/blocks';
+
+import { ComponentHeader, LinkTo } from '~styleguide/blocks';
+
+import * as ButtonStories from './Button.stories';
+
+export const parameters = {
+ subtitle: `Button atoms for specfic use cases.`,
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/file/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=1106%3A0',
+ },
+ status: 'current',
+ source: {
+ repo: 'gamut',
+ githubLink:
+ 'https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/Button/index.ts',
+ },
+};
+
+
+
+
+
+## Usage
+
+We have several versions of buttons that you can use.
+Each supports _light_ and _dark_ mode.
+
+See the [Figma designs](https://www.figma.com/file/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=1106%3A0) for a nice grid view of all the variants.
+All but CTA button support a size of either `normal` (default) or `small`.
+
+### Best practices:
+
+- Use `primary` for high priority actions (e.g. Submit).
+- Use `secondary` for low priority buttons (e.g. Close).
+
+### When NOT to use:
+
+- Avoid using a color mode outside of its context (no light buttons on navy backgrounds)
+
+
+## Variants
+The examples below use `FillButton` to showcase the `primary` and `secondary` button variants.
+### Primary
+
+
+### Secondary
+
+
+## Inline icons
+Our FillButton , StrokeButton , and TextButton all support a single inline icon. You can align the icon to the left or right of the button text using the `iconPosition` prop. These icons should be from the mini set to be legible at a smaller size.
+
+
+
+## Buttons in light and dark mode
+Below are examples of how buttons could look in their respective light and dark modes.
+
+### Light mode
+
+
+
+### Dark mode
+
+
+
+## UX writing
+
+Writing words for a link or button? Above all else, make sure that the words alone make it clear where a button or link will take the user. Refer to the checklist below for guidance. For more tips and best practices, check out the full guide about writing for buttons and links.
+
+* Make sure it is clear where the link or button will take the user.
+* Use sentence case, capitalizing only proper nouns.
+* For buttons, start with an action-oriented verb (like “explore” or “view”).
+* For buttons, use 3 words or less (unless more words are necessary for clarity).
diff --git a/packages/styleguide/src/lib/Atoms/Buttons/Button/Button.stories.tsx b/packages/styleguide/src/lib/Atoms/Buttons/Button/Button.stories.tsx
new file mode 100644
index 0000000000..4ca1e2d739
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Buttons/Button/Button.stories.tsx
@@ -0,0 +1,122 @@
+import {
+ Box,
+ FillButton,
+ GridBox,
+ IconButton,
+ StrokeButton,
+ TextButton,
+} from '@codecademy/gamut';
+import {
+ MiniArrowLeftIcon,
+ MiniArrowRightIcon,
+ MiniDeleteIcon,
+ MiniRibbonIcon,
+ SearchIcon,
+} from '@codecademy/gamut-icons';
+import type { Meta, StoryObj } from '@storybook/react';
+
+// Using FillButton here to show the difference between primary and secondary variants
+// however, this could be any Button
+const meta: Meta = {
+ component: FillButton,
+ args: {},
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Primary: Story = {
+ args: {
+ variant: 'primary',
+ children: 'Submit',
+ },
+};
+
+export const Secondary: Story = {
+ args: {
+ variant: 'secondary',
+ children: 'Cancel',
+ },
+};
+
+const InlineIconsExample = () => {
+ return (
+
+
+ FillButton
+
+
+ Leading icon
+
+
+ TextButton
+
+
+ );
+};
+
+export const InlineIcons: Story = {
+ render: () => ,
+};
+
+const buttons = [FillButton, IconButton, StrokeButton, TextButton] as const;
+const variants = ['primary', 'secondary'] as const;
+const sizes = ['normal', 'small', 'large'] as const;
+
+const ButtonScale = ({ mode }: { mode: 'dark' | 'light' }) => {
+ const grid = buttons.map(({ displayName }) => (
+ {displayName}
+ ));
+ variants.forEach((variant: (typeof variants)[number]) => {
+ sizes.forEach((size) => {
+ buttons.forEach((Component) => {
+ const props = {
+ key: `${Component.displayName}-${mode}-${variant}-${size}`,
+ mode,
+ variant,
+ size,
+ icon: MiniDeleteIcon,
+ tip: 'n/a',
+ };
+ if (Component.displayName === 'IconButton') {
+ return grid.push(
+
+ );
+ }
+ grid.push({mode});
+ });
+ });
+ });
+
+ return (
+
+ {grid}
+
+ );
+};
+
+export const ButtonsLightMode: Story = {
+ render: () => ,
+};
+
+export const ButtonsDarkMode: Story = {
+ render: () => ,
+};
diff --git a/packages/styleguide/src/lib/Atoms/Buttons/CTAButton/CTAButton.mdx b/packages/styleguide/src/lib/Atoms/Buttons/CTAButton/CTAButton.mdx
new file mode 100644
index 0000000000..cd5e275347
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Buttons/CTAButton/CTAButton.mdx
@@ -0,0 +1,33 @@
+import { Canvas, Controls, Meta } from '@storybook/blocks';
+
+import { ComponentHeader } from '~styleguide/blocks';
+
+import * as CTAButtonStories from './CTAButton.stories';
+
+export const parameters = {
+ subtitle: `A "Call to Action" button that is used to prompt users to take a specific action.`,
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/file/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=1106%3A0',
+ },
+ status: 'current',
+ source: {
+ repo: 'gamut',
+ githubLink:
+ 'https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/Button/CTAButton.tsx',
+ },
+};
+
+
+
+
+
+## Usage
+
+Use this for high-visibility marketing actions. Include a leading or trailing icons from our mini icon set to clarify an action.
+
+## Playground
+
+
+
+
diff --git a/packages/styleguide/src/lib/Atoms/Buttons/CTAButton/CTAButton.stories.tsx b/packages/styleguide/src/lib/Atoms/Buttons/CTAButton/CTAButton.stories.tsx
new file mode 100644
index 0000000000..f6f01940ac
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Buttons/CTAButton/CTAButton.stories.tsx
@@ -0,0 +1,36 @@
+import { CTAButton } from '@codecademy/gamut';
+import type { Meta, StoryObj } from '@storybook/react';
+
+const meta: Meta = {
+ component: CTAButton,
+ args: {
+ children: 'Click Me',
+ disabled: false,
+ size: 'normal',
+ },
+ argTypes: {
+ href: {
+ description: 'If defined, component will use an anchor tag',
+ },
+ mode: {
+ control: {
+ type: 'select',
+ options: ['dark', 'light'],
+ },
+ },
+ size: {
+ control: {
+ type: 'select',
+ options: ['normal', 'small', 'large'],
+ },
+ }
+ }
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {},
+};
+
diff --git a/packages/styleguide/src/lib/Atoms/Buttons/FillButton/FillButton.mdx b/packages/styleguide/src/lib/Atoms/Buttons/FillButton/FillButton.mdx
new file mode 100644
index 0000000000..2496ef6444
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Buttons/FillButton/FillButton.mdx
@@ -0,0 +1,31 @@
+import { Canvas, Controls, Meta } from '@storybook/blocks';
+
+import { ComponentHeader } from '~styleguide/blocks';
+
+import * as FillButtonStories from './FillButton.stories';
+
+export const parameters = {
+ subtitle: `A button that has a solid background color and is used for primary actions.`,
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/file/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=1106%3A0',
+ },
+ status: 'current',
+ source: {
+ repo: 'gamut',
+ githubLink:
+ 'https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/Button/FillButton.tsx',
+ },
+};
+
+
+
+
+
+## Usage
+Use this for primary actions. Include a leading or trailing icons from our mini icon set to clarify an action.
+
+## Playground
+
+
+
diff --git a/packages/styleguide/src/lib/Atoms/Buttons/FillButton/FillButton.stories.tsx b/packages/styleguide/src/lib/Atoms/Buttons/FillButton/FillButton.stories.tsx
new file mode 100644
index 0000000000..5b0c71e90b
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Buttons/FillButton/FillButton.stories.tsx
@@ -0,0 +1,42 @@
+import { FillButton } from '@codecademy/gamut';
+import { SearchIcon } from '@codecademy/gamut-icons';
+import type { Meta, StoryObj } from '@storybook/react';
+
+const meta: Meta = {
+ component: FillButton,
+ args: {
+ children: 'Click Me',
+ disabled: false,
+ size: 'normal',
+ },
+ argTypes: {
+ href: {
+ description: 'If defined, component will use an anchor tag',
+ },
+ mode: {
+ control: {
+ type: 'select',
+ options: ['dark', 'light'],
+ },
+ },
+ size: {
+ control: {
+ type: 'select',
+ options: ['normal', 'small', 'large'],
+ },
+ },
+ icon: {
+ control: {
+ options: [SearchIcon],
+ },
+ },
+ }
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {},
+};
+
diff --git a/packages/styleguide/src/lib/Atoms/Buttons/IconButton/IconButton.mdx b/packages/styleguide/src/lib/Atoms/Buttons/IconButton/IconButton.mdx
new file mode 100644
index 0000000000..dfaebfbd88
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Buttons/IconButton/IconButton.mdx
@@ -0,0 +1,31 @@
+import { Canvas, Controls, Meta } from '@storybook/blocks';
+
+import { ComponentHeader } from '~styleguide/blocks';
+
+import * as IconButtonStories from './IconButton.stories';
+
+export const parameters = {
+ subtitle: `A button that displays a clickable icon.`,
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/file/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=1106%3A0',
+ },
+ status: 'current',
+ source: {
+ repo: 'gamut',
+ githubLink:
+ 'https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/Button/IconButton.tsx',
+ },
+};
+
+
+
+
+
+## Usage
+Use for secondary or space-constrained actions with recognizable icons. Use regular icons for normal buttons and mini icons for small buttons. Icon-only buttons must include an aria-label, which will also appear in the tooltip on hover and focus.
+
+## Playground
+
+
+
diff --git a/packages/styleguide/src/lib/Atoms/Buttons/IconButton/IconButton.stories.tsx b/packages/styleguide/src/lib/Atoms/Buttons/IconButton/IconButton.stories.tsx
new file mode 100644
index 0000000000..8387a5adde
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Buttons/IconButton/IconButton.stories.tsx
@@ -0,0 +1,40 @@
+import { IconButton } from '@codecademy/gamut';
+import { SearchIcon } from '@codecademy/gamut-icons';
+import type { Meta, StoryObj } from '@storybook/react';
+
+const meta: Meta = {
+ component: IconButton,
+ args: {
+ children: 'Click Me',
+ disabled: false,
+ size: 'normal',
+ icon: SearchIcon,
+ tip: "ToolTip",
+ tipProps: { placement: 'floating' }
+ },
+ argTypes: {
+ href: {
+ description: 'If defined, component will use an anchor tag',
+ },
+ mode: {
+ control: {
+ type: 'select',
+ options: ['dark', 'light'],
+ },
+ },
+ size: {
+ control: {
+ type: 'select',
+ options: ['normal', 'small', 'large'],
+ },
+ },
+ }
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {},
+};
+
diff --git a/packages/styleguide/src/lib/Atoms/Buttons/StrokeButton/StrokeButton.mdx b/packages/styleguide/src/lib/Atoms/Buttons/StrokeButton/StrokeButton.mdx
new file mode 100644
index 0000000000..7467799908
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Buttons/StrokeButton/StrokeButton.mdx
@@ -0,0 +1,31 @@
+import { Canvas, Controls, Meta } from '@storybook/blocks';
+
+import { ComponentHeader } from '~styleguide/blocks';
+
+import * as StrokeButtonStories from './StrokeButton.stories';
+
+export const parameters = {
+ subtitle: `A button that has a outline around its text and is used for secondary actions.`,
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/file/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=1106%3A0',
+ },
+ status: 'current',
+ source: {
+ repo: 'gamut',
+ githubLink:
+ 'https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/Button/StrokeButton.tsx',
+ },
+};
+
+
+
+
+
+## Usage
+Use this for secondary actions. Include a leading or trailing icons from our mini icon set to clarify an action.
+
+## Playground
+
+
+
diff --git a/packages/styleguide/src/lib/Atoms/Buttons/StrokeButton/StrokeButton.stories.tsx b/packages/styleguide/src/lib/Atoms/Buttons/StrokeButton/StrokeButton.stories.tsx
new file mode 100644
index 0000000000..b77a187b17
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Buttons/StrokeButton/StrokeButton.stories.tsx
@@ -0,0 +1,42 @@
+import { StrokeButton } from '@codecademy/gamut';
+import { SearchIcon } from '@codecademy/gamut-icons';
+import type { Meta, StoryObj } from '@storybook/react';
+
+const meta: Meta = {
+ component: StrokeButton,
+ args: {
+ children: 'Click Me',
+ disabled: false,
+ size: 'normal',
+ },
+ argTypes: {
+ href: {
+ description: 'If defined, component will use an anchor tag',
+ },
+ mode: {
+ control: {
+ type: 'select',
+ options: ['dark', 'light'],
+ },
+ },
+ size: {
+ control: {
+ type: 'select',
+ options: ['normal', 'small', 'large'],
+ },
+ },
+ icon: {
+ control: {
+ options: [SearchIcon],
+ },
+ },
+ }
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {},
+};
+
diff --git a/packages/styleguide/src/lib/Atoms/Buttons/TextButton/TextButton.mdx b/packages/styleguide/src/lib/Atoms/Buttons/TextButton/TextButton.mdx
new file mode 100644
index 0000000000..03d18c1567
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Buttons/TextButton/TextButton.mdx
@@ -0,0 +1,37 @@
+import { Canvas, Controls, Meta } from '@storybook/blocks';
+
+import { ComponentHeader } from '~styleguide/blocks';
+
+import * as TextButtonStories from './TextButton.stories';
+
+export const parameters = {
+ subtitle: `A button that usually only has text and is used for tertiary actions.`,
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/file/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=1106%3A0',
+ },
+ status: 'current',
+ source: {
+ repo: 'gamut',
+ githubLink:
+ 'https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/Button/TextButton.tsx',
+ },
+};
+
+
+
+
+
+## Usage
+Use this for tertiary actions. Include a leading or trailing icons from our mini icon set to clarify an action or to distinguish the button from other bold text.
+
+### Best practices:
+
+Our text buttons use bold formatting to differentiate themselves from normal text, as color alone does not meet the 3:1 contrast ratio required for accessibility. However, bold formatting may not be sufficient in all contexts, such as when placed alongside a bold heading.
+- In these cases, we recommend including a leading or trailing icon to further distinguish the button from other bold text. This additional visual cue ensures that the text button is easily identifiable and accessible to all users.
+
+
+## Playground
+
+
+
diff --git a/packages/styleguide/src/lib/Atoms/Buttons/TextButton/TextButton.stories.tsx b/packages/styleguide/src/lib/Atoms/Buttons/TextButton/TextButton.stories.tsx
new file mode 100644
index 0000000000..b7f96fbd53
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Buttons/TextButton/TextButton.stories.tsx
@@ -0,0 +1,42 @@
+import { TextButton } from '@codecademy/gamut';
+import { SearchIcon } from '@codecademy/gamut-icons';
+import type { Meta, StoryObj } from '@storybook/react';
+
+const meta: Meta = {
+ component: TextButton,
+ args: {
+ children: 'Click Me',
+ disabled: false,
+ size: 'normal',
+ },
+ argTypes: {
+ href: {
+ description: 'If defined, component will use an anchor tag',
+ },
+ mode: {
+ control: {
+ type: 'select',
+ options: ['dark', 'light'],
+ },
+ },
+ size: {
+ control: {
+ type: 'select',
+ options: ['normal', 'small', 'large'],
+ },
+ },
+ icon: {
+ control: {
+ options: [SearchIcon],
+ },
+ },
+ }
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {},
+};
+
diff --git a/packages/styleguide/src/lib/Atoms/Card/Card.mdx b/packages/styleguide/src/lib/Atoms/Card/Card.mdx
new file mode 100644
index 0000000000..4fc02ffe9e
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Card/Card.mdx
@@ -0,0 +1,83 @@
+import { Canvas, Controls, Meta } from '@storybook/blocks';
+
+import { ComponentHeader, LinkTo } from '~styleguide/blocks';
+
+import * as CardStories from './Card.stories';
+
+export const parameters = {
+ subtitle:
+ 'A card with multiple background combinations for presenting different types of information.',
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=29975-39392&node-type=frame&t=SgphNxlCwEFrcv0X-0',
+ },
+ status: 'updating',
+ source: {
+ repo: 'gamut',
+ githubLink:
+ 'https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/Card',
+ },
+};
+
+
+
+
+
+## Usage
+
+Use Card to present different types of information in a visually appealing way.
+
+## Variants
+
+### White
+
+
+
+### Yellow
+
+
+
+### Navy
+
+
+
+### Hyper
+
+
+
+### Beige
+
+
+
+## Shadow variants
+
+### Small
+
+
+
+### Medium
+
+
+
+### Outline
+
+The outline color will inherit the color of the card.
+
+
+
+
+## As link
+
+
+
+## ColorMode as default
+
+Card elements respond to the current ColorMode they are used in. Note that their background color will match the background of their context and secondary colors are used for their hover effect. As these are not the typical inverse effects we utilize in color mode, please check with your design team before use.
+
+
+
+## Playground
+
+
+
+
diff --git a/packages/styleguide/src/lib/Atoms/Card/Card.stories.tsx b/packages/styleguide/src/lib/Atoms/Card/Card.stories.tsx
new file mode 100644
index 0000000000..24df5a04b5
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Card/Card.stories.tsx
@@ -0,0 +1,119 @@
+import { Anchor, Card, Column, LayoutGrid } from '@codecademy/gamut';
+import { Background } from '@codecademy/gamut-styles';
+import type { Meta, StoryObj } from '@storybook/react';
+
+const meta: Meta = {
+ component: Card,
+ args: {
+ children: 'Hello world I am a Card',
+ p: 16,
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {},
+};
+
+export const White: Story = {
+ args: {
+ variant: 'white',
+ },
+};
+
+export const Yellow: Story = {
+ args: {
+ variant: 'yellow',
+ },
+};
+
+export const Navy: Story = {
+ args: {
+ variant: 'navy',
+ },
+};
+
+export const Hyper: Story = {
+ args: {
+ variant: 'hyper',
+ },
+};
+
+export const Beige: Story = {
+ args: {
+ variant: 'beige',
+ },
+};
+
+export const SmallShadow: Story = {
+ args: {
+ shadow: 'small',
+ },
+};
+
+export const MediumShadow: Story = {
+ args: {
+ shadow: 'medium',
+ },
+};
+
+export const OutlineShadow: Story = {
+ args: {
+ shadow: 'outline',
+ },
+};
+
+export const OutlineShadowColor: Story = {
+ args: {
+ shadow: 'outline',
+ variant: 'yellow',
+ },
+};
+
+export const Link: Story = {
+ render: (args) => (
+
+
+
+ ),
+ args: {
+ shadow: 'medium',
+ },
+};
+
+export const Dynamic: Story = {
+ render: () => (
+
+
+
+
+ Click Me!
+
+
+
+
+
+
+ Click Me!
+
+
+
+
+ ),
+};
diff --git a/packages/styleguide/src/lib/Atoms/Drawer/Drawer.mdx b/packages/styleguide/src/lib/Atoms/Drawer/Drawer.mdx
new file mode 100644
index 0000000000..cb78a1e64b
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Drawer/Drawer.mdx
@@ -0,0 +1,41 @@
+import { Canvas, Controls, Meta } from '@storybook/blocks';
+
+import { ComponentHeader, LinkTo } from '~styleguide/blocks';
+
+import * as DrawerStories from './Drawer.stories';
+
+export const parameters = {
+ subtitle:
+ 'Animated container that expands horizontally to show its contents.',
+ status: 'current',
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=30420-41014&node-type=frame&t=SgphNxlCwEFrcv0X-0',
+ },
+ source: {
+ repo: 'gamut',
+ githubLink:
+ 'https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/Drawer',
+ },
+};
+
+
+
+
+
+## Usage
+
+Drawers animate between having `0` width and having a responsive width:
+
+- Mobile devices (<`sm`): `75vw`
+- Tablet and larger (>=`sm`): `30rem`
+
+An example of a molecule that uses Drawer is the Flyout.
+
+Our Drawers are [controlled components](https://reactjs.org/docs/forms.html#controlled-components), so their checked value must be controlled by an external state passed in as `expanded`.
+
+## Playground
+
+
+
+
diff --git a/packages/styleguide/src/lib/Atoms/Drawer/Drawer.stories.tsx b/packages/styleguide/src/lib/Atoms/Drawer/Drawer.stories.tsx
new file mode 100644
index 0000000000..acfb94e4bc
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/Drawer/Drawer.stories.tsx
@@ -0,0 +1,24 @@
+import { Drawer, FlexBox, StrokeButton } from '@codecademy/gamut';
+import type { Meta } from '@storybook/react';
+import { useState } from 'react';
+
+const meta: Meta = {
+ component: Drawer,
+ args: {},
+};
+
+export default meta;
+
+export const Default: React.FC = () => {
+ const [expanded, setExpanded] = useState(false);
+ return (
+
+ Drawer content in here!
+ setExpanded((previousExpanded) => !previousExpanded)}
+ >
+ Toggle Drawer
+
+
+ );
+};
diff --git a/packages/styleguide/src/lib/Atoms/FloatingCard/FloatingCard.mdx b/packages/styleguide/src/lib/Atoms/FloatingCard/FloatingCard.mdx
new file mode 100644
index 0000000000..ac788543c5
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/FloatingCard/FloatingCard.mdx
@@ -0,0 +1,77 @@
+import { Canvas, Controls, Meta } from '@storybook/blocks';
+
+import { ComponentHeader, LinkTo } from '~styleguide/blocks';
+
+import * as FloatingCardStories from './FloatingCard.stories';
+
+export const parameters = {
+ subtitle:
+ 'A card with a persistent patterned shadow meant for user feedback or interaction outside the normal flow.',
+ status: 'current',
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=29975-39392&node-type=frame&t=SgphNxlCwEFrcv0X-0',
+ },
+ source: {
+ repo: 'gamut',
+ githubLink:
+ 'https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/FloatingCard',
+ },
+};
+
+
+
+
+
+## Usage
+
+- They have patterned drop shadow to indicate that are outside the normal document flow.
+- This is a shared component used to create `Toast`, `Dialog`, and `Coachmark`; before using this component directly please check that these components do not cover your use case!
+- `Tooltip` shares many styles with FloatingCards but is used more widely and does not have a shadow as that might detract from the critical functions it serves.
+
+## Beaks
+
+FloatingCards can optionally display a beak. This is used to indicate a point of interest to the user - that the content of the card describes. Beaks are primarily used in `Coachmarks`.
+
+
+
+
+
+
+## Shadow direction
+
+There are 2 types of shadow directions. The default shadow offset is bottom left.
+
+
+
+
+## Shadow pattern
+
+We can specify the pattern of the FloatingCard shadow by providing the `pattern` prop with the imported pattern from `@codecademy/gamut-patterns`.
+
+See the `Patterns` story to view all the possible pattern options.
+
+
+
+## Card wrapper display
+
+By default a `FloatingCard` has a wrapper with a display of `inline-block`. However, if you need to you can set the `containerDisplay` to `block`, as shown below:
+
+```tsx
+import { FloatingCard } from '@codecademy/gamut';
+import { DotDense } from '@codecademy/gamut-patterns';
+
+export const MyComponent: React.FC = () => {
+ return (
+
+ Take up all the space.
+
+ );
+};
+```
+
+## Playground
+
+
+
+
diff --git a/packages/styleguide/src/lib/Atoms/FloatingCard/FloatingCard.stories.tsx b/packages/styleguide/src/lib/Atoms/FloatingCard/FloatingCard.stories.tsx
new file mode 100644
index 0000000000..ac8abe3a46
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/FloatingCard/FloatingCard.stories.tsx
@@ -0,0 +1,65 @@
+import { FloatingCard } from '@codecademy/gamut';
+import { DotDense } from '@codecademy/gamut-patterns';
+import type { Meta, StoryObj } from '@storybook/react';
+
+const meta: Meta = {
+ component: FloatingCard,
+ args: {
+ children: "Yakety Yak don't don't talk back!",
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {},
+};
+
+export const BeakBottomLeft: Story = {
+ args: {
+ beak: 'bottom-left',
+ children: 'Beak Bottom Left',
+ },
+};
+
+export const BeakBottomRight: Story = {
+ args: {
+ beak: 'bottom-right',
+ children: 'Beak Bottom Right',
+ },
+};
+
+export const BeakTopLeft: Story = {
+ args: {
+ beak: 'top-left',
+ children: 'Beak Top Left',
+ },
+};
+
+export const BeakTopRight: Story = {
+ args: {
+ beak: 'top-right',
+ children: 'Beak Top Right',
+ },
+};
+
+export const ShadowBottomLeft: Story = {
+ args: {
+ shadow: 'bottomLeft',
+ children: 'Shadow Bottom Left',
+ },
+};
+
+export const ShadowBottomRight: Story = {
+ args: {
+ shadow: 'bottomRight',
+ children: 'Shadow Bottom Right',
+ },
+};
+
+export const ShadowPattern: Story = {
+ args: {
+ pattern: DotDense,
+ },
+};
diff --git a/packages/styleguide/src/lib/Atoms/FocusTrap/FocusTrap.mdx b/packages/styleguide/src/lib/Atoms/FocusTrap/FocusTrap.mdx
new file mode 100644
index 0000000000..2656e56bbd
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/FocusTrap/FocusTrap.mdx
@@ -0,0 +1,30 @@
+import { Canvas, Controls, Meta } from '@storybook/blocks';
+
+import { ComponentHeader } from '~styleguide/blocks';
+
+import * as FocusTrapStories from './FocusTrap.stories';
+
+export const parameters = {
+ subtitle:
+ "Used for modal and dialog experiences like `Overlay` and `ModalDeprecated`, this component controls focus management behavior when we need to limit focus to a single area of the page for some amount of time. It doesn't need to be a modal or overlay, and can instead be controlled with the `active` prop.",
+ status: 'current',
+ source: {
+ repo: 'gamut',
+ githubLink:
+ 'https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/FocusTrap',
+ },
+};
+
+
+
+
+
+## Usage
+
+FocusTrap is a wrapper around the [react-focus-on](https://github.com/theKashey/react-focus-on) library, so any necessary functionality that provides should be exposed through this component. If necessary, you can pass props directly to `react-focus-on` through the `focusOnProps` prop.
+
+## Playground
+
+
+
+
diff --git a/packages/styleguide/src/lib/Atoms/FocusTrap/FocusTrap.stories.tsx b/packages/styleguide/src/lib/Atoms/FocusTrap/FocusTrap.stories.tsx
new file mode 100644
index 0000000000..269d7e31ad
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/FocusTrap/FocusTrap.stories.tsx
@@ -0,0 +1,56 @@
+import { Box, FillButton, FocusTrap, GridForm } from '@codecademy/gamut';
+import type { Meta } from '@storybook/react';
+import { useState } from 'react';
+
+const meta: Meta = {
+ component: FocusTrap,
+ args: {},
+};
+
+export default meta;
+
+export const Default: React.FC = () => {
+ const [trapActive, setActive] = useState(false);
+ return (
+ <>
+
+ setActive(true)}>
+ Enable Focus Trap
+
+
+
+ {
+ setActive(false);
+ }}
+ submit={{
+ contents: 'Disable Focus Trap',
+ position: 'right',
+ size: 12,
+ }}
+ />
+
+
+
+ >
+ );
+};
diff --git a/packages/styleguide/src/lib/Atoms/FormElements/About.mdx b/packages/styleguide/src/lib/Atoms/FormElements/About.mdx
new file mode 100644
index 0000000000..203e85fd0e
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/FormElements/About.mdx
@@ -0,0 +1,64 @@
+import { Meta } from '@storybook/blocks';
+
+import { AboutHeader, LinkTo, TableOfContents } from '~styleguide/blocks';
+
+export const parameters = {
+ id: 'Atoms/FormElements',
+ title: 'FormElements',
+ subtitle:
+ 'Ancillary descriptions for input atoms tied 1:1 with values to be submitted in a form.',
+};
+
+
+
+
+
+You should generally never use these directly as a Gamut consumer. While our `Form` atoms compose the structure of these forms, we strongly recommend only creating forms with using `GridForm` or `ConnectedForm`. You should only use the form element atoms as individual elements if your interface doesn’t need to support the technical functions of a form.
+
+Both of our Form organisms provide the following benefits:
+
+1. **Accessibility**: All our forms handle accessibility styling and behaviors, passing tests out-of-the-box.
+2. **Functionality**: Validation and submission logic is handled by the [react-hook-form](https://react-hook-form.com) library
+
+We recommend GridForm if you have a form that sticks to a 12x12 visual grid with consistent vertical rhythm and grid spacing.
+
+For everything else, we recommend ConnectedForm.
+
+
+` element for wrapping other form elements.',
+ title: 'Form',
+ status: 'current',
+ },
+ {
+ id: 'Atoms/FormElements/FormGroup',
+ subtitle: 'A label and description wrapper for a form element that provides conditional styling.',
+ title: 'FormGroup',
+ status: 'current',
+ },
+ {
+ id: 'Atoms/FormElements/FormGroupDescription',
+ subtitle: 'A description wrapper to format a longer description of a group of inputs.',
+ title: 'FormGroupDescription',
+ status: 'current',
+ },
+ {
+ id: 'Atoms/FormElements/FormGroupLabel',
+ subtitle: "A label element to be tied to a group of elements.",
+ title: 'FormGroupLabel',
+ status: 'current',
+ },
+ {
+ id: 'Atoms/FormElements/FormRequiredText',
+ subtitle: "A Text element that renders our required field explanation, should be included in all forms with required fields.",
+ title: 'FormRequiredText',
+ status: 'current',
+ }
+ ]}
+/>
+
+
+
diff --git a/packages/styleguide/src/lib/Atoms/FormElements/Form/Form.mdx b/packages/styleguide/src/lib/Atoms/FormElements/Form/Form.mdx
new file mode 100644
index 0000000000..fd4e26b6cc
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/FormElements/Form/Form.mdx
@@ -0,0 +1,33 @@
+import { Canvas, Controls, Meta } from '@storybook/blocks';
+
+import { ComponentHeader, LinkTo } from '~styleguide/blocks';
+
+import * as FormStories from './Form.stories';
+
+export const parameters = {
+ subtitle: 'A customizable `` element for wrapping other form elements',
+ status: 'current',
+ source: {
+ repo: 'gamut',
+ githubLink:
+ 'https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/Form/elements/Form.tsx'
+ },
+};
+
+
+
+
+
+## Usage
+
+This is `Form` atom is the base component for other forms like the organisms: GridForm and ConnectedForm.
+
+
+### Best practices:
+- You should not use this component directly, instead use GridForm or ConnectedForm depending on your needs.
+
+## Playground
+
+
+
+
diff --git a/packages/styleguide/src/lib/Atoms/FormElements/Form/Form.stories.tsx b/packages/styleguide/src/lib/Atoms/FormElements/Form/Form.stories.tsx
new file mode 100644
index 0000000000..43273253d6
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/FormElements/Form/Form.stories.tsx
@@ -0,0 +1,16 @@
+import { Form } from '@codecademy/gamut';
+import type { Meta, StoryObj } from '@storybook/react';
+
+const meta: Meta = {
+ component: Form,
+ args: {},
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {
+ children: 'This is an empty form',
+ },
+};
diff --git a/packages/styleguide/src/lib/Atoms/FormElements/FormGroup/FormGroup.mdx b/packages/styleguide/src/lib/Atoms/FormElements/FormGroup/FormGroup.mdx
new file mode 100644
index 0000000000..4663aba90b
--- /dev/null
+++ b/packages/styleguide/src/lib/Atoms/FormElements/FormGroup/FormGroup.mdx
@@ -0,0 +1,64 @@
+import { Canvas, Controls, Meta } from '@storybook/blocks';
+
+import { ComponentHeader, LinkTo } from '~styleguide/blocks';
+
+import * as FormGroupStories from './FormGroup.stories';
+
+export const parameters = {
+ subtitle: `A label and description wrapper for a form element that provides conditional styling.`,
+ status: 'current',
+ source: {
+ repo: 'gamut',
+ githubLink:
+ 'https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/Form/elements/FormGroup.tsx',
+ },
+};
+
+
+
+
+
+## Usage
+
+Use `FormGroup` to provide a label and description for a form element.
+
+## Specifications
+
+### `htmlFor`
+
+It is best to provide `htmlFor` to both the FormGroup + inner form element to make it super accessible. If this is not provided, the FormLabel will default to ``. If an `htmlFor` is provided, it will be a `