├── .github
└── workflows
│ └── validate.yml
├── .gitignore
├── .npmrc
├── .prettierignore
├── .vscode
├── extensions.json
└── settings.json
├── LICENSE.md
├── README.md
├── epicshop
├── .npmrc
├── Dockerfile
├── fix-watch.js
├── fix.js
├── fly.toml
├── package-lock.json
├── package.json
├── setup-custom.js
├── setup.js
├── test.js
└── update-deps.sh
├── eslint.config.js
├── exercises
├── 01.figma-to-tailwind
│ ├── 01.problem.design-implementation
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 01.solution.design-implementation
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── FINISHED.mdx
│ └── README.mdx
├── 02.color-tokens
│ ├── 01.problem.custom-colors-in-ui
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 01.solution.custom-colors-in-ui
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 02.problem.cutoff-default-colors
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 02.solution.cutoff-default-colors
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 03.problem.non-colors-support
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 03.solution.non-colors-support
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 04.problem.semantic-design-tokens
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 04.solution.semantic-design-tokens
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 05.problem.broader-scopes
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 05.solution.broader-scopes
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 06.problem.css-variables
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── components
│ │ │ │ └── Demo.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 06.solution.css-variables
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── components
│ │ │ │ └── Demo.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 07.problem.dark-mode
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── components
│ │ │ │ └── Demo.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 07.solution.dark-mode
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── components
│ │ │ │ └── Demo.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 08.problem.multi-theme
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── components
│ │ │ │ └── Demo.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 08.solution.multi-theme
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── components
│ │ │ │ └── Demo.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── FINISHED.mdx
│ └── README.mdx
├── 03.tailwind-v4
│ ├── 01.problem.theme-config-in-css
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── components
│ │ │ │ └── Demo.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 01.solution.theme-config-in-css
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── components
│ │ │ │ └── Demo.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 02.problem.simplification
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── components
│ │ │ │ └── Demo.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 02.solution.simplification
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── components
│ │ │ │ └── Demo.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── FINISHED.mdx
│ ├── README.mdx
│ └── instructor.png
├── FINISHED.mdx
└── README.mdx
├── package-lock.json
├── package.json
├── public
└── images
│ ├── color-swatches-vscode.png
│ ├── color-tokens-usage.png
│ ├── dark-mode.png
│ ├── devmode-2.png
│ ├── devmode.png
│ ├── highlight-text-eww.png
│ ├── instructor.png
│ ├── raw-color-tokens.png
│ ├── semantic-color-tokens.png
│ └── token-layers.png
└── tsconfig.json
/.github/workflows/validate.yml:
--------------------------------------------------------------------------------
1 | name: deploy
2 |
3 | concurrency:
4 | group: ${{ github.workflow }}-${{ github.ref }}
5 | cancel-in-progress: true
6 |
7 | on:
8 | push:
9 | branches:
10 | - 'main'
11 | pull_request:
12 | branches:
13 | - 'main'
14 | jobs:
15 | setup:
16 | strategy:
17 | matrix:
18 | os: [ubuntu-latest, windows-latest, macos-latest]
19 | runs-on: ${{ matrix.os }}
20 | steps:
21 | - name: ⬇️ Checkout repo
22 | uses: actions/checkout@v4
23 |
24 | - name: ⎔ Setup node
25 | uses: actions/setup-node@v4
26 | with:
27 | node-version: 20
28 |
29 | - name: ▶️ Run setup script
30 | run: npm run setup
31 |
32 | - name: ʦ TypeScript
33 | run: npm run typecheck
34 |
35 | - name: ⬣ ESLint
36 | run: npm run lint
37 |
38 | deploy:
39 | name: 🚀 Deploy
40 | runs-on: ubuntu-latest
41 | # only deploy main branch on pushes on non-forks
42 | if:
43 | ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' &&
44 | github.repository_owner == 'epicweb-dev' }}
45 |
46 | steps:
47 | - name: ⬇️ Checkout repo
48 | uses: actions/checkout@v4
49 |
50 | - name: 🎈 Setup Fly
51 | uses: superfly/flyctl-actions/setup-flyctl@1.5
52 |
53 | - name: 🚀 Deploy
54 | run: flyctl deploy --remote-only
55 | working-directory: ./epicshop
56 | env:
57 | FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
58 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | workspace/
4 | **/.cache/
5 | **/build/
6 | **/public/build
7 | **/playwright-report
8 | data.db
9 | /playground
10 | **/tsconfig.tsbuildinfo
11 |
12 | # in a real app you'd want to not commit the .env
13 | # file as well, but since this is for a workshop
14 | # we're going to keep them around.
15 | # .env
16 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | legacy-peer-deps=true
2 | registry=https://registry.npmjs.org/
3 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | **/node_modules/**
2 | **/.cache/**
3 | **/build/**
4 | **/dist/**
5 | **/public/build/**
6 | **/package-lock.json
7 | **/playwright-report/**
8 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "dbaeumer.vscode-eslint",
4 | "esbenp.prettier-vscode",
5 | "bradlc.vscode-tailwindcss",
6 | "neotan.vscode-auto-restart-typescript-eslint-servers",
7 | "prisma.prisma",
8 | "qwtel.sqlite-viewer"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "typescript.preferences.autoImportFileExcludePatterns": [
3 | "@remix-run/server-runtime",
4 | "@remix-run/router",
5 | "react-router-dom",
6 | "react-router"
7 | ],
8 | "workbench.editorAssociations": {
9 | "*.db": "sqlite-viewer.view"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | This material is available for private, non-commercial use under the
2 | [GPL version 3](http://www.gnu.org/licenses/gpl-3.0-standalone.html). If you
3 | would like to use this material to conduct your own workshop, please contact us
4 | at team@epicweb.dev
5 |
--------------------------------------------------------------------------------
/epicshop/.npmrc:
--------------------------------------------------------------------------------
1 | legacy-peer-deps=true
2 | registry=https://registry.npmjs.org/
3 |
--------------------------------------------------------------------------------
/epicshop/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:20-bookworm-slim as base
2 |
3 | RUN apt-get update && apt-get install -y git
4 |
5 | ENV EPICSHOP_CONTEXT_CWD="/myapp/workshop-content"
6 | ENV EPICSHOP_DEPLOYED="true"
7 | ENV EPICSHOP_DISABLE_WATCHER="true"
8 | ENV FLY="true"
9 | ENV PORT="8080"
10 | ENV NODE_ENV="production"
11 |
12 | WORKDIR /myapp
13 |
14 | ADD . .
15 |
16 | RUN npm install --omit=dev
17 |
18 | CMD rm -rf ${EPICSHOP_CONTEXT_CWD} && \
19 | git clone https://github.com/epicweb-dev/tailwindcss-color-tokens ${EPICSHOP_CONTEXT_CWD} && \
20 | cd ${EPICSHOP_CONTEXT_CWD} && \
21 | npx epicshop start
22 |
--------------------------------------------------------------------------------
/epicshop/fix-watch.js:
--------------------------------------------------------------------------------
1 | import path from 'node:path'
2 | import { fileURLToPath } from 'node:url'
3 | import chokidar from 'chokidar'
4 | import { $ } from 'execa'
5 |
6 | const __dirname = path.dirname(fileURLToPath(import.meta.url))
7 | const here = (...p) => path.join(__dirname, ...p)
8 |
9 | const workshopRoot = here('..')
10 |
11 | const watchPath = path.join(workshopRoot, './exercises/*')
12 | const watcher = chokidar.watch(watchPath, {
13 | ignored: /(^|[\/\\])\../, // ignore dotfiles
14 | persistent: true,
15 | ignoreInitial: true,
16 | depth: 2,
17 | })
18 |
19 | const debouncedRun = debounce(run, 200)
20 |
21 | // Add event listeners.
22 | watcher
23 | .on('addDir', path => {
24 | debouncedRun()
25 | })
26 | .on('unlinkDir', path => {
27 | // Only act if path contains two slashes (excluding the leading `./`)
28 | debouncedRun()
29 | })
30 | .on('error', error => console.log(`Watcher error: ${error}`))
31 |
32 | /**
33 | * Simple debounce implementation
34 | */
35 | function debounce(fn, delay) {
36 | let timer = null
37 | return (...args) => {
38 | if (timer) clearTimeout(timer)
39 | timer = setTimeout(() => {
40 | fn(...args)
41 | }, delay)
42 | }
43 | }
44 |
45 | let running = false
46 |
47 | async function run() {
48 | if (running) {
49 | console.log('still running...')
50 | return
51 | }
52 | running = true
53 | try {
54 | await $({
55 | stdio: 'inherit',
56 | cwd: workshopRoot,
57 | })`node ./epicshop/fix.js`
58 | } catch (error) {
59 | throw error
60 | } finally {
61 | running = false
62 | }
63 | }
64 |
65 | console.log(`watching ${watchPath}`)
66 |
67 | // doing this because the watcher doesn't seem to work and I don't have time
68 | // to figure out why 🙃
69 | console.log('Polling...')
70 | setInterval(() => {
71 | run()
72 | }, 1000)
73 |
74 | console.log('running fix to start...')
75 | run()
76 |
--------------------------------------------------------------------------------
/epicshop/fly.toml:
--------------------------------------------------------------------------------
1 | app = "epicweb-dev-tailwindcss-color-tokens"
2 | primary_region = "sjc"
3 | kill_signal = "SIGINT"
4 | kill_timeout = 5
5 | processes = [ ]
6 | swap_size_mb = 512
7 |
8 | [experimental]
9 | allowed_public_ports = [ ]
10 | auto_rollback = true
11 |
12 | [[services]]
13 | internal_port = 8080
14 | processes = [ "app" ]
15 | protocol = "tcp"
16 | script_checks = [ ]
17 |
18 | [services.concurrency]
19 | hard_limit = 100
20 | soft_limit = 80
21 | type = "connections"
22 |
23 | [[services.ports]]
24 | handlers = [ "http" ]
25 | port = 80
26 | force_https = true
27 |
28 | [[services.ports]]
29 | handlers = [ "tls", "http" ]
30 | port = 443
31 |
32 | [[services.tcp_checks]]
33 | grace_period = "1s"
34 | interval = "15s"
35 | restart_limit = 0
36 | timeout = "2s"
37 |
38 | [[services.http_checks]]
39 | interval = "10s"
40 | grace_period = "5s"
41 | method = "get"
42 | path = "/"
43 | protocol = "http"
44 | timeout = "2s"
45 | tls_skip_verify = false
46 | headers = { }
--------------------------------------------------------------------------------
/epicshop/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "dependencies": {
4 | "@epic-web/workshop-app": "^5.7.1",
5 | "@epic-web/workshop-utils": "^5.7.1",
6 | "chokidar": "^3.6.0",
7 | "execa": "^9.3.0",
8 | "fs-extra": "^11.2.0"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/epicshop/setup-custom.js:
--------------------------------------------------------------------------------
1 | import path from 'node:path'
2 | import {
3 | getApps,
4 | isProblemApp,
5 | setPlayground,
6 | } from '@epic-web/workshop-utils/apps.server'
7 | import fsExtra from 'fs-extra'
8 |
9 | const allApps = await getApps()
10 | const problemApps = allApps.filter(isProblemApp)
11 |
12 | if (!process.env.SKIP_PLAYGROUND) {
13 | const firstProblemApp = problemApps[0]
14 | if (firstProblemApp) {
15 | console.log('🛝 setting up the first problem app...')
16 | const playgroundPath = path.join(process.cwd(), 'playground')
17 | if (await fsExtra.exists(playgroundPath)) {
18 | console.log('🗑 deleting existing playground app')
19 | await fsExtra.remove(playgroundPath)
20 | }
21 | await setPlayground(firstProblemApp.fullPath).then(
22 | () => {
23 | console.log('✅ first problem app set up')
24 | },
25 | (error) => {
26 | console.error(error)
27 | throw new Error('❌ first problem app setup failed')
28 | },
29 | )
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/epicshop/setup.js:
--------------------------------------------------------------------------------
1 | import { spawnSync } from 'child_process'
2 |
3 | const styles = {
4 | // got these from playing around with what I found from:
5 | // https://github.com/istanbuljs/istanbuljs/blob/0f328fd0896417ccb2085f4b7888dd8e167ba3fa/packages/istanbul-lib-report/lib/file-writer.js#L84-L96
6 | // they're the best I could find that works well for light or dark terminals
7 | success: { open: '\u001b[32;1m', close: '\u001b[0m' },
8 | danger: { open: '\u001b[31;1m', close: '\u001b[0m' },
9 | info: { open: '\u001b[36;1m', close: '\u001b[0m' },
10 | subtitle: { open: '\u001b[2;1m', close: '\u001b[0m' },
11 | }
12 |
13 | function color(modifier, string) {
14 | return styles[modifier].open + string + styles[modifier].close
15 | }
16 |
17 | console.log(color('info', '▶️ Starting workshop setup...'))
18 |
19 | const output = spawnSync('npm --version', { shell: true })
20 | .stdout.toString()
21 | .trim()
22 | const outputParts = output.split('.')
23 | const major = Number(outputParts[0])
24 | const minor = Number(outputParts[1])
25 | if (major < 8 || (major === 8 && minor < 16)) {
26 | console.error(
27 | color(
28 | 'danger',
29 | '🚨 npm version is ' +
30 | output +
31 | ' which is out of date. Please install npm@8.16.0 or greater',
32 | ),
33 | )
34 | throw new Error('npm version is out of date')
35 | }
36 |
37 | const command =
38 | 'npx --yes "https://gist.github.com/kentcdodds/bb452ffe53a5caa3600197e1d8005733" -q'
39 | console.log(
40 | color('subtitle', ' Running the following command: ' + command),
41 | )
42 |
43 | const result = spawnSync(command, { stdio: 'inherit', shell: true })
44 |
45 | if (result.status === 0) {
46 | console.log(color('success', '✅ Workshop setup complete...'))
47 | } else {
48 | process.exit(result.status)
49 | }
50 |
51 | /*
52 | eslint
53 | "no-undef": "off",
54 | "vars-on-top": "off",
55 | */
56 |
--------------------------------------------------------------------------------
/epicshop/update-deps.sh:
--------------------------------------------------------------------------------
1 | npx npm-check-updates --dep prod,dev --upgrade --workspaces --root
2 | cd epicshop && npx npm-check-updates --dep prod,dev --upgrade --root
3 | cd ..
4 | rm -rf node_modules package-lock.json ./epicshop/package-lock.json ./epicshop/node_modules ./exercises/**/node_modules
5 | npm install
6 | npm run setup
7 | npm run typecheck
8 | npm run lint --fix
9 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import defaultConfig from '@epic-web/config/eslint'
2 |
3 | /** @type {import("eslint").Linter.Config} */
4 | export default [
5 | ...defaultConfig,
6 | {
7 | languageOptions: {
8 | parserOptions: {
9 | projectService: {
10 | maximumDefaultProjectFileMatchCount_THIS_WILL_SLOW_DOWN_LINTING: 1000,
11 | },
12 | },
13 | },
14 | rules: {
15 | // we leave unused vars around for the exercises
16 | 'no-unused-vars': 'off',
17 | '@typescript-eslint/no-unused-vars': 'off',
18 | },
19 | },
20 | ]
21 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.problem.design-implementation/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.problem.design-implementation/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_01.figma-to-tailwind_01.problem.design-implementation",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.14.10",
18 | "@types/react": "^18.3.3",
19 | "@types/react-dom": "^18.3.0",
20 | "@typescript-eslint/eslint-plugin": "^7.16.0",
21 | "@typescript-eslint/parser": "^7.16.0",
22 | "@vitejs/plugin-react": "^4.3.1",
23 | "autoprefixer": "^10.4.19",
24 | "eslint": "^9.6.0",
25 | "eslint-plugin-react-hooks": "^4.6.2",
26 | "eslint-plugin-react-refresh": "^0.4.8",
27 | "postcss": "^8.4.39",
28 | "prettier": "^3.3.2",
29 | "prettier-plugin-tailwindcss": "^0.6.5",
30 | "tailwindcss": "^3.4.4",
31 | "typescript": "^5.5.3",
32 | "vite": "^5.3.3"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.problem.design-implementation/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.problem.design-implementation/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.problem.design-implementation/src/App.tsx:
--------------------------------------------------------------------------------
1 | export default function ColorDesignTokens() {
2 | return (
3 |
4 | {/* 🐨 Replace the paragraph below with the responsive UI */}
5 |
Build the Figma design here...
6 |
7 | )
8 | }
9 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.problem.design-implementation/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.problem.design-implementation/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.problem.design-implementation/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.problem.design-implementation/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import { type Config } from 'tailwindcss'
2 |
3 | export default {
4 | content: ['index.html', './src/**/*.{js,ts,jsx,tsx}'],
5 | theme: {
6 | extend: {},
7 | },
8 | plugins: [],
9 | } satisfies Config
10 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.problem.design-implementation/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.problem.design-implementation/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.problem.design-implementation/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.problem.design-implementation/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react'
2 | import { defineConfig } from 'vite'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | server: {
8 | port: Number(process.env.PORT) || 3000,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.solution.design-implementation/README.mdx:
--------------------------------------------------------------------------------
1 | # Design Implementation
2 |
3 |
4 |
5 | Nice one — how did you go?
6 |
7 | With that out of the way, let's start bringing the custom colors to our Tailwind CSS project.
8 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.solution.design-implementation/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.solution.design-implementation/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_01.figma-to-tailwind_01.solution.design-implementation",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.14.10",
18 | "@types/react": "^18.3.3",
19 | "@types/react-dom": "^18.3.0",
20 | "@typescript-eslint/eslint-plugin": "^7.16.0",
21 | "@typescript-eslint/parser": "^7.16.0",
22 | "@vitejs/plugin-react": "^4.3.1",
23 | "autoprefixer": "^10.4.19",
24 | "eslint": "^9.6.0",
25 | "eslint-plugin-react-hooks": "^4.6.2",
26 | "eslint-plugin-react-refresh": "^0.4.8",
27 | "postcss": "^8.4.39",
28 | "prettier": "^3.3.2",
29 | "prettier-plugin-tailwindcss": "^0.6.5",
30 | "tailwindcss": "^3.4.4",
31 | "typescript": "^5.5.3",
32 | "vite": "^5.3.3"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.solution.design-implementation/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.solution.design-implementation/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.solution.design-implementation/src/App.tsx:
--------------------------------------------------------------------------------
1 | export default function ColorDesignTokens() {
2 | return (
3 |
4 |
5 |
6 |
7 | Special Deal
8 |
9 |
10 | Opportunity of a lifetime
11 |
12 |
13 | Lorem ipsum dolor sit, amet consectetur adipisicing elit. Voluptates
14 | sint dicta quas explicabo rem quisquam consectetur aperiam facere
15 | temporibus aut. Voluptatibus minus nesciunt qui voluptas fugiat
16 | voluptate repudiandae error asperiores.
17 |
18 |
19 |
23 | Learn more
24 |
25 |
26 |
27 |
28 |
29 | Conversion rate
30 |
31 |
32 | 9.86%
33 |
34 |
35 |
36 |
37 | customers
38 |
39 |
40 | 1M+
41 |
42 |
43 |
44 |
45 |
46 | )
47 | }
48 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.solution.design-implementation/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.solution.design-implementation/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.solution.design-implementation/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.solution.design-implementation/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import { type Config } from 'tailwindcss'
2 |
3 | export default {
4 | content: ['index.html', './src/**/*.{js,ts,jsx,tsx}'],
5 | theme: {
6 | extend: {},
7 | },
8 | plugins: [],
9 | } satisfies Config
10 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.solution.design-implementation/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.solution.design-implementation/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.solution.design-implementation/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/01.solution.design-implementation/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react'
2 | import { defineConfig } from 'vite'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | server: {
8 | port: Number(process.env.PORT) || 3000,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/FINISHED.mdx:
--------------------------------------------------------------------------------
1 | # Figma to Tailwind CSS
2 |
3 | Nice work!
4 |
5 | Got some feedback on the exercise you've just completed? Please take a minute to fill the feedback form.
6 |
7 | This helps improve the quality of the materials!
8 |
9 |
10 | Want to take your **Figma-to-Tailwind CSS conversion skills** to the next
11 | level? The [Pixel Perfect
12 | Tailwind](https://www.epicweb.dev/workshops/pixel-perfect-figma-to-tailwind)
13 | workshop is a great next step.
14 |
15 |
16 | Allright, take a quick break, and let's jump into the next exercise!
17 |
--------------------------------------------------------------------------------
/exercises/01.figma-to-tailwind/README.mdx:
--------------------------------------------------------------------------------
1 | # Figma to Tailwind CSS
2 |
3 |
4 |
5 | This exercise is about converting the Figma design to a Tailwind CSS project — both for the mobile and desktop layouts.
6 |
7 | We're not going to worry about the colors just yet — but we'll build a "grayscale" high fidelity version of the designs.
8 |
9 |
15 |
16 | When you're ready, hit the "Start Learning" button.
17 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.problem.custom-colors-in-ui/README.mdx:
--------------------------------------------------------------------------------
1 | # Custom colors in UI
2 |
3 |
4 |
5 | ## 1. Add custom theme colors
6 |
7 | Start by extending the Tailwind theme with the raw color tokens.
8 |
9 | You can do this in the file.
10 |
11 | ```ts
12 | export default {
13 | theme: {
14 | extend: {
15 | colors: {
16 | // Add the colors here...
17 | },
18 | },
19 | },
20 | }
21 | ```
22 |
23 | There should be 14 new colors added to the theme:
24 |
25 | - `teal`
26 | - `purple`
27 | - `grey-0`
28 | - `grey-5`
29 | - `grey-10`
30 | - `grey-20`
31 | - `grey-30`
32 | - `grey-40`
33 | - `grey-50`
34 | - `grey-60`
35 | - `grey-70`
36 | - `grey-80`
37 | - `grey-90`
38 | - `grey-100`
39 |
40 | ## 2. Replace the colors in the UI
41 |
42 | Update the UI to use the color tokens to match the design.
43 |
44 | Remember, we'll change to semantic design tookens later — but let's get the UI right first with these new colors in place.
45 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.problem.custom-colors-in-ui/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.problem.custom-colors-in-ui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_02.color-tokens_01.problem.custom-colors-in-ui",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.14.10",
18 | "@types/react": "^18.3.3",
19 | "@types/react-dom": "^18.3.0",
20 | "@typescript-eslint/eslint-plugin": "^7.16.0",
21 | "@typescript-eslint/parser": "^7.16.0",
22 | "@vitejs/plugin-react": "^4.3.1",
23 | "autoprefixer": "^10.4.19",
24 | "eslint": "^9.6.0",
25 | "eslint-plugin-react-hooks": "^4.6.2",
26 | "eslint-plugin-react-refresh": "^0.4.8",
27 | "postcss": "^8.4.39",
28 | "prettier": "^3.3.2",
29 | "prettier-plugin-tailwindcss": "^0.6.5",
30 | "tailwindcss": "^3.4.4",
31 | "typescript": "^5.5.3",
32 | "vite": "^5.3.3"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.problem.custom-colors-in-ui/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.problem.custom-colors-in-ui/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.problem.custom-colors-in-ui/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.problem.custom-colors-in-ui/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.problem.custom-colors-in-ui/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.problem.custom-colors-in-ui/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import { type Config } from 'tailwindcss'
2 |
3 | export default {
4 | content: ['index.html', './src/**/*.{js,ts,jsx,tsx}'],
5 | theme: {
6 | extend: {
7 | // 🐨 Add the custom colors here
8 | },
9 | },
10 | plugins: [],
11 | } satisfies Config
12 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.problem.custom-colors-in-ui/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.problem.custom-colors-in-ui/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.problem.custom-colors-in-ui/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.problem.custom-colors-in-ui/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react'
2 | import { defineConfig } from 'vite'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | server: {
8 | port: Number(process.env.PORT) || 3000,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.solution.custom-colors-in-ui/README.mdx:
--------------------------------------------------------------------------------
1 | # Custom colors in UI
2 |
3 |
4 |
5 | Looking good! 😘
6 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.solution.custom-colors-in-ui/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.solution.custom-colors-in-ui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_02.color-tokens_01.solution.custom-colors-in-ui",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.14.10",
18 | "@types/react": "^18.3.3",
19 | "@types/react-dom": "^18.3.0",
20 | "@typescript-eslint/eslint-plugin": "^7.16.0",
21 | "@typescript-eslint/parser": "^7.16.0",
22 | "@vitejs/plugin-react": "^4.3.1",
23 | "autoprefixer": "^10.4.19",
24 | "eslint": "^9.6.0",
25 | "eslint-plugin-react-hooks": "^4.6.2",
26 | "eslint-plugin-react-refresh": "^0.4.8",
27 | "postcss": "^8.4.39",
28 | "prettier": "^3.3.2",
29 | "prettier-plugin-tailwindcss": "^0.6.5",
30 | "tailwindcss": "^3.4.4",
31 | "typescript": "^5.5.3",
32 | "vite": "^5.3.3"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.solution.custom-colors-in-ui/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.solution.custom-colors-in-ui/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.solution.custom-colors-in-ui/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.solution.custom-colors-in-ui/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.solution.custom-colors-in-ui/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.solution.custom-colors-in-ui/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import { type Config } from 'tailwindcss'
2 |
3 | export default {
4 | content: ['index.html', './src/**/*.{js,ts,jsx,tsx}'],
5 | theme: {
6 | extend: {
7 | colors: {
8 | teal: '#00FFE1',
9 | purple: '#6200FF',
10 | grey: {
11 | 0: '#FFFFFF',
12 | 5: '#EBEBEB',
13 | 10: '#DADADA',
14 | 20: '#C2C2C2',
15 | 30: '#AAAAAA',
16 | 40: '#919191',
17 | 50: '#797979',
18 | 60: '#616161',
19 | 70: '#494949',
20 | 80: '#313131',
21 | 90: '#181818',
22 | 100: '#000000',
23 | },
24 | },
25 | },
26 | },
27 | plugins: [],
28 | } satisfies Config
29 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.solution.custom-colors-in-ui/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.solution.custom-colors-in-ui/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.solution.custom-colors-in-ui/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/01.solution.custom-colors-in-ui/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react'
2 | import { defineConfig } from 'vite'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | server: {
8 | port: Number(process.env.PORT) || 3000,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.problem.cutoff-default-colors/README.mdx:
--------------------------------------------------------------------------------
1 | # Cutoff Default Colors
2 |
3 |
4 | By extending the theme, we still hace access to all the default colors.
5 |
6 | While that's very useful for prototyping, in this case we don't want devlopers to start using `zinc` instead our custom `grey`, or `indigo` instead of our `accent`.
7 |
8 | Make sure the only colors available in the theme are the custom ones you've added.
9 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.problem.cutoff-default-colors/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.problem.cutoff-default-colors/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_02.color-tokens_02.problem.cutoff-default-colors",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.14.10",
18 | "@types/react": "^18.3.3",
19 | "@types/react-dom": "^18.3.0",
20 | "@typescript-eslint/eslint-plugin": "^7.16.0",
21 | "@typescript-eslint/parser": "^7.16.0",
22 | "@vitejs/plugin-react": "^4.3.1",
23 | "autoprefixer": "^10.4.19",
24 | "eslint": "^9.6.0",
25 | "eslint-plugin-react-hooks": "^4.6.2",
26 | "eslint-plugin-react-refresh": "^0.4.8",
27 | "postcss": "^8.4.39",
28 | "prettier": "^3.3.2",
29 | "prettier-plugin-tailwindcss": "^0.6.5",
30 | "tailwindcss": "^3.4.4",
31 | "typescript": "^5.5.3",
32 | "vite": "^5.3.3"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.problem.cutoff-default-colors/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.problem.cutoff-default-colors/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.problem.cutoff-default-colors/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.problem.cutoff-default-colors/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.problem.cutoff-default-colors/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.problem.cutoff-default-colors/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import { type Config } from 'tailwindcss'
2 |
3 | export default {
4 | content: ['index.html', './src/**/*.{js,ts,jsx,tsx}'],
5 | theme: {
6 | extend: {
7 | // 🐨 Override the theme colors instead of extending them
8 | colors: {
9 | teal: '#00FFE1',
10 | purple: '#6200FF',
11 | grey: {
12 | 0: '#FFFFFF',
13 | 5: '#EBEBEB',
14 | 10: '#DADADA',
15 | 20: '#C2C2C2',
16 | 30: '#AAAAAA',
17 | 40: '#919191',
18 | 50: '#797979',
19 | 60: '#616161',
20 | 70: '#494949',
21 | 80: '#313131',
22 | 90: '#181818',
23 | 100: '#000000',
24 | },
25 | },
26 | },
27 | },
28 | plugins: [],
29 | } satisfies Config
30 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.problem.cutoff-default-colors/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.problem.cutoff-default-colors/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.problem.cutoff-default-colors/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.problem.cutoff-default-colors/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react'
2 | import { defineConfig } from 'vite'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | server: {
8 | port: Number(process.env.PORT) || 3000,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.solution.cutoff-default-colors/README.mdx:
--------------------------------------------------------------------------------
1 | # Cutoff Default Colors
2 |
3 |
4 |
5 | Good stuff — less room to make a mess 😅
6 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.solution.cutoff-default-colors/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.solution.cutoff-default-colors/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_02.color-tokens_02.solution.cutoff-default-colors",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.14.10",
18 | "@types/react": "^18.3.3",
19 | "@types/react-dom": "^18.3.0",
20 | "@typescript-eslint/eslint-plugin": "^7.16.0",
21 | "@typescript-eslint/parser": "^7.16.0",
22 | "@vitejs/plugin-react": "^4.3.1",
23 | "autoprefixer": "^10.4.19",
24 | "eslint": "^9.6.0",
25 | "eslint-plugin-react-hooks": "^4.6.2",
26 | "eslint-plugin-react-refresh": "^0.4.8",
27 | "postcss": "^8.4.39",
28 | "prettier": "^3.3.2",
29 | "prettier-plugin-tailwindcss": "^0.6.5",
30 | "tailwindcss": "^3.4.4",
31 | "typescript": "^5.5.3",
32 | "vite": "^5.3.3"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.solution.cutoff-default-colors/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.solution.cutoff-default-colors/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.solution.cutoff-default-colors/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.solution.cutoff-default-colors/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.solution.cutoff-default-colors/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.solution.cutoff-default-colors/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import { type Config } from 'tailwindcss'
2 |
3 | export default {
4 | content: ['index.html', './src/**/*.{js,ts,jsx,tsx}'],
5 | theme: {
6 | extend: {},
7 | colors: {
8 | teal: '#00FFE1',
9 | purple: '#6200FF',
10 | grey: {
11 | 0: '#FFFFFF',
12 | 5: '#EBEBEB',
13 | 10: '#DADADA',
14 | 20: '#C2C2C2',
15 | 30: '#AAAAAA',
16 | 40: '#919191',
17 | 50: '#797979',
18 | 60: '#616161',
19 | 70: '#494949',
20 | 80: '#313131',
21 | 90: '#181818',
22 | 100: '#000000',
23 | },
24 | },
25 | },
26 | plugins: [],
27 | } satisfies Config
28 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.solution.cutoff-default-colors/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.solution.cutoff-default-colors/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.solution.cutoff-default-colors/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/02.solution.cutoff-default-colors/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react'
2 | import { defineConfig } from 'vite'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | server: {
8 | port: Number(process.env.PORT) || 3000,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.problem.non-colors-support/README.mdx:
--------------------------------------------------------------------------------
1 | # "Non colors" support
2 |
3 |
4 |
5 | It might look like everything is working properly — but with our `colors` override, we have accidentally removed some functionality.
6 |
7 | For example, try to make the button's background transparent on hover:
8 |
9 | ```html
10 | Learn more
11 | ```
12 |
13 | If you hover this button, you'd expect the background to become... transparent, right?
14 |
15 | Womp Womp. It doesn't work.
16 |
17 | ### The default colors are more than just _colors_.
18 |
19 | The `colors` object in the Tailwind theme contains a few things beside "true" colors.
20 |
21 | Go ahead, have a look at the [Tailwind docs (text-color, for example)](https://tailwindcss.com/docs/text-color) to see what's in there.
22 |
23 | Yup. `inherit`, `current`, `transparent`.
24 |
25 | Well, we've removed all three by overriding the `colors` object.
26 |
27 | And that's why our `bg-transparent` class has no effect.
28 |
29 | ## Bring these back
30 |
31 | Make sure these three key-value pairs are also added to the colors object:
32 |
33 | ```ts
34 | {
35 | inherit: 'inherit',
36 | transparent: 'transparent',
37 | current: 'currentColor',
38 | }
39 | ```
40 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.problem.non-colors-support/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.problem.non-colors-support/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_02.color-tokens_03.problem.non-colors-support",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.14.10",
18 | "@types/react": "^18.3.3",
19 | "@types/react-dom": "^18.3.0",
20 | "@typescript-eslint/eslint-plugin": "^7.16.0",
21 | "@typescript-eslint/parser": "^7.16.0",
22 | "@vitejs/plugin-react": "^4.3.1",
23 | "autoprefixer": "^10.4.19",
24 | "eslint": "^9.6.0",
25 | "eslint-plugin-react-hooks": "^4.6.2",
26 | "eslint-plugin-react-refresh": "^0.4.8",
27 | "postcss": "^8.4.39",
28 | "prettier": "^3.3.2",
29 | "prettier-plugin-tailwindcss": "^0.6.5",
30 | "tailwindcss": "^3.4.4",
31 | "typescript": "^5.5.3",
32 | "vite": "^5.3.3"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.problem.non-colors-support/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.problem.non-colors-support/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.problem.non-colors-support/src/App.tsx:
--------------------------------------------------------------------------------
1 | export default function ColorDesignTokens() {
2 | return (
3 |
4 |
5 |
6 |
7 | Special Deal
8 |
9 |
10 | Opportunity of a lifetime
11 |
12 |
13 | Lorem ipsum dolor sit, amet consectetur adipisicing elit. Voluptates
14 | sint dicta quas explicabo rem quisquam consectetur aperiam facere
15 | temporibus aut. Voluptatibus minus nesciunt qui voluptas fugiat
16 | voluptate repudiandae error asperiores.
17 |
18 |
19 |
23 | Learn more
24 |
25 |
26 |
27 |
28 |
29 | Conversion rate
30 |
31 |
32 | 9.86%
33 |
34 |
35 |
36 |
37 | customers
38 |
39 |
40 | 1M+
41 |
42 |
43 |
44 |
45 |
46 | )
47 | }
48 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.problem.non-colors-support/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.problem.non-colors-support/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.problem.non-colors-support/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.problem.non-colors-support/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import { type Config } from 'tailwindcss'
2 |
3 | export default {
4 | content: ['index.html', './src/**/*.{js,ts,jsx,tsx}'],
5 | theme: {
6 | extend: {},
7 | /*
8 | 🐨 Bring back support for `inherit`, `transparent`
9 | and `currentColor` in the `colors` object.
10 | */
11 | colors: {
12 | teal: '#00FFE1',
13 | purple: '#6200FF',
14 | grey: {
15 | 0: '#FFFFFF',
16 | 5: '#EBEBEB',
17 | 10: '#DADADA',
18 | 20: '#C2C2C2',
19 | 30: '#AAAAAA',
20 | 40: '#919191',
21 | 50: '#797979',
22 | 60: '#616161',
23 | 70: '#494949',
24 | 80: '#313131',
25 | 90: '#181818',
26 | 100: '#000000',
27 | },
28 | },
29 | },
30 | plugins: [],
31 | } satisfies Config
32 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.problem.non-colors-support/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.problem.non-colors-support/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.problem.non-colors-support/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.problem.non-colors-support/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react'
2 | import { defineConfig } from 'vite'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | server: {
8 | port: Number(process.env.PORT) || 3000,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.solution.non-colors-support/README.mdx:
--------------------------------------------------------------------------------
1 | # "Non colors" support
2 |
3 |
4 |
5 | Awesome save — that would have created some sneaky bugs down the road that would get developers scratching their head!
6 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.solution.non-colors-support/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.solution.non-colors-support/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_02.color-tokens_03.solution.non-colors-support",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.14.10",
18 | "@types/react": "^18.3.3",
19 | "@types/react-dom": "^18.3.0",
20 | "@typescript-eslint/eslint-plugin": "^7.16.0",
21 | "@typescript-eslint/parser": "^7.16.0",
22 | "@vitejs/plugin-react": "^4.3.1",
23 | "autoprefixer": "^10.4.19",
24 | "eslint": "^9.6.0",
25 | "eslint-plugin-react-hooks": "^4.6.2",
26 | "eslint-plugin-react-refresh": "^0.4.8",
27 | "postcss": "^8.4.39",
28 | "prettier": "^3.3.2",
29 | "prettier-plugin-tailwindcss": "^0.6.5",
30 | "tailwindcss": "^3.4.4",
31 | "typescript": "^5.5.3",
32 | "vite": "^5.3.3"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.solution.non-colors-support/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.solution.non-colors-support/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.solution.non-colors-support/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.solution.non-colors-support/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.solution.non-colors-support/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.solution.non-colors-support/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import { type Config } from 'tailwindcss'
2 |
3 | export default {
4 | content: ['index.html', './src/**/*.{js,ts,jsx,tsx}'],
5 | theme: {
6 | extend: {},
7 | colors: {
8 | inherit: 'inherit',
9 | transparent: 'transparent',
10 | current: 'currentColor',
11 | teal: '#00FFE1',
12 | purple: '#6200FF',
13 | grey: {
14 | 0: '#FFFFFF',
15 | 5: '#EBEBEB',
16 | 10: '#DADADA',
17 | 20: '#C2C2C2',
18 | 30: '#AAAAAA',
19 | 40: '#919191',
20 | 50: '#797979',
21 | 60: '#616161',
22 | 70: '#494949',
23 | 80: '#313131',
24 | 90: '#181818',
25 | 100: '#000000',
26 | },
27 | },
28 | },
29 | plugins: [],
30 | } satisfies Config
31 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.solution.non-colors-support/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.solution.non-colors-support/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.solution.non-colors-support/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/03.solution.non-colors-support/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react'
2 | import { defineConfig } from 'vite'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | server: {
8 | port: Number(process.env.PORT) || 3000,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.problem.semantic-design-tokens/README.mdx:
--------------------------------------------------------------------------------
1 | # Semantic Design Tokens
2 |
3 |
4 |
5 | We've got our colors under control.
6 |
7 | Well — actually we still have a problem with the current setup. Nothing is stopping anyone to use the `highlight` (`teal`) color (meant for background surfaces) on a text element, for example.
8 |
9 | ```html
10 | Special Deal
11 | ```
12 |
13 |
14 |
15 | Ewwww...
16 |
17 | What's thaaaat?
18 |
19 | 😅
20 |
21 | I don't think the designers wanted the `highlight` color to be used like that.
22 |
23 | ## Scope colors to specific use cases
24 |
25 | We can lock down the usage of colors to specific scenarios by leveraging Tailwind's `corePlugins`.
26 |
27 | The `colors` object makes a color available **everywhere**.
28 |
29 | But you can target core plugins like `backgroundColor` to only allow colors to be used for _backgrounds_.
30 |
31 | Or `borderColor` for borders, etc.
32 |
33 | ## Translate the Figma file's Semantic Color Tokens
34 |
35 | Recreate the semantic tokens you can find in Figma in your Tailwind theme.
36 |
37 | If you can't access the variables panel in Figma, here's a screenshot of the relevant color _mapping_.
38 |
39 |
40 |
41 | ## Update the UI to use the new, semantic tokens
42 |
43 | Make sure the UI is using the new semantic tokens and still works properly!
44 |
45 | You've got this — good luck!
46 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.problem.semantic-design-tokens/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.problem.semantic-design-tokens/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_02.color-tokens_04.problem.semantic-design-tokens",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.14.10",
18 | "@types/react": "^18.3.3",
19 | "@types/react-dom": "^18.3.0",
20 | "@typescript-eslint/eslint-plugin": "^7.16.0",
21 | "@typescript-eslint/parser": "^7.16.0",
22 | "@vitejs/plugin-react": "^4.3.1",
23 | "autoprefixer": "^10.4.19",
24 | "eslint": "^9.6.0",
25 | "eslint-plugin-react-hooks": "^4.6.2",
26 | "eslint-plugin-react-refresh": "^0.4.8",
27 | "postcss": "^8.4.39",
28 | "prettier": "^3.3.2",
29 | "prettier-plugin-tailwindcss": "^0.6.5",
30 | "tailwindcss": "^3.4.4",
31 | "typescript": "^5.5.3",
32 | "vite": "^5.3.3"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.problem.semantic-design-tokens/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.problem.semantic-design-tokens/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.problem.semantic-design-tokens/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.problem.semantic-design-tokens/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.problem.semantic-design-tokens/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.problem.semantic-design-tokens/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import { type Config } from 'tailwindcss'
2 |
3 | export default {
4 | content: ['index.html', './src/**/*.{js,ts,jsx,tsx}'],
5 | theme: {
6 | colors: {
7 | inherit: 'inherit',
8 | transparent: 'transparent',
9 | current: 'currentColor',
10 | /*
11 | 🐨 Move the colors below to a `colors`
12 | object at the top of the file.
13 | */
14 | teal: '#00FFE1',
15 | purple: '#6200FF',
16 | grey: {
17 | 0: '#FFFFFF',
18 | 5: '#EBEBEB',
19 | 10: '#DADADA',
20 | 20: '#C2C2C2',
21 | 30: '#AAAAAA',
22 | 40: '#919191',
23 | 50: '#797979',
24 | 60: '#616161',
25 | 70: '#494949',
26 | 80: '#313131',
27 | 90: '#181818',
28 | 100: '#000000',
29 | },
30 | },
31 | /*
32 | 🐨 Extend the theme with semantic tokens defined for:
33 | - Background colors (backgroundColor)
34 | - Border colors (borderColor)
35 | - Text colors (textColor)
36 |
37 | These tokens should consume the "raw" colors you defined at the top.
38 | */
39 | },
40 | plugins: [],
41 | } satisfies Config
42 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.problem.semantic-design-tokens/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.problem.semantic-design-tokens/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.problem.semantic-design-tokens/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.problem.semantic-design-tokens/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react'
2 | import { defineConfig } from 'vite'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | server: {
8 | port: Number(process.env.PORT) || 3000,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.solution.semantic-design-tokens/README.mdx:
--------------------------------------------------------------------------------
1 | # Semantic Design Tokens
2 |
3 |
4 |
5 | Going places! Our Tailwind color setup is now much more controlled and rigid.
6 |
7 | Rigid and controlled are _good_ things if you're setting up an abmitious and themable color system for a project.
8 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.solution.semantic-design-tokens/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.solution.semantic-design-tokens/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_02.color-tokens_04.solution.semantic-design-tokens",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.14.10",
18 | "@types/react": "^18.3.3",
19 | "@types/react-dom": "^18.3.0",
20 | "@typescript-eslint/eslint-plugin": "^7.16.0",
21 | "@typescript-eslint/parser": "^7.16.0",
22 | "@vitejs/plugin-react": "^4.3.1",
23 | "autoprefixer": "^10.4.19",
24 | "eslint": "^9.6.0",
25 | "eslint-plugin-react-hooks": "^4.6.2",
26 | "eslint-plugin-react-refresh": "^0.4.8",
27 | "postcss": "^8.4.39",
28 | "prettier": "^3.3.2",
29 | "prettier-plugin-tailwindcss": "^0.6.5",
30 | "tailwindcss": "^3.4.4",
31 | "typescript": "^5.5.3",
32 | "vite": "^5.3.3"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.solution.semantic-design-tokens/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.solution.semantic-design-tokens/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.solution.semantic-design-tokens/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.solution.semantic-design-tokens/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.solution.semantic-design-tokens/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.solution.semantic-design-tokens/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import { type Config } from 'tailwindcss'
2 |
3 | const colors = {
4 | teal: '#00FFE1',
5 | purple: '#6200FF',
6 | grey: {
7 | 0: '#FFFFFF',
8 | 5: '#EBEBEB',
9 | 10: '#DADADA',
10 | 20: '#C2C2C2',
11 | 30: '#AAAAAA',
12 | 40: '#919191',
13 | 50: '#797979',
14 | 60: '#616161',
15 | 70: '#494949',
16 | 80: '#313131',
17 | 90: '#181818',
18 | 100: '#000000',
19 | },
20 | }
21 |
22 | export default {
23 | content: ['index.html', './src/**/*.{js,ts,jsx,tsx}'],
24 | theme: {
25 | colors: {
26 | inherit: 'inherit',
27 | transparent: 'transparent',
28 | current: 'currentColor',
29 | },
30 | extend: {
31 | backgroundColor: {
32 | highlight: colors.teal,
33 | accent: colors.purple,
34 | neutral: {
35 | DEFAULT: colors.grey['0'],
36 | inverted: colors.grey['100'],
37 | },
38 | subtle: colors.grey['5'],
39 | bold: colors.grey['80'],
40 | },
41 | textColor: {
42 | copy: colors.grey['100'],
43 | subtle: colors.grey['60'],
44 | muted: colors.grey['40'],
45 | inverted: colors.grey['5'],
46 | },
47 | borderColor: {
48 | bold: colors.grey['60'],
49 | subtle: colors.grey['40'],
50 | muted: colors.grey['20'],
51 | },
52 | },
53 | },
54 | plugins: [],
55 | } satisfies Config
56 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.solution.semantic-design-tokens/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.solution.semantic-design-tokens/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.solution.semantic-design-tokens/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/04.solution.semantic-design-tokens/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react'
2 | import { defineConfig } from 'vite'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | server: {
8 | port: Number(process.env.PORT) || 3000,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.problem.broader-scopes/README.mdx:
--------------------------------------------------------------------------------
1 | # Broader Scopes
2 |
3 |
4 |
5 | You know how we accidentally broke background transparency in a previous exercise?
6 |
7 | Well, we've broken things again, without meaning to.
8 |
9 | Here's an example: try make the `highlight` box have a gradient background fading from `highlight` to white (`bg-neutral`):
10 |
11 | ```html
12 | ...
13 | ```
14 |
15 | Uuuuh — `from-` and `to-` classes only suggest `inherit`, `current`, and `transparent` as possible values.
16 |
17 | Yup. The colors we added to the `backgroundColor` core plugin are only available for... background colors.
18 |
19 | ## Broaden the scope of where colors are allowed
20 |
21 | Background gradient options are controlled by the `gradientColorStops` corePlugin. The background colors should probably be added there too.
22 |
23 | What about `outline` or `ring` utilities? Sounds like they should share the `borderColor` options, right?
24 |
25 | There is a decent amount of `color`-related [corePlugins in Tailwind](https://tailwindcss.com/docs/theme#configuration-reference). You don't need to add **all** of them in this exercise, but add a few.
26 |
27 | And remember this moment — because you'll almost inevitably reach a point where some color somewhere doesn't work, because you've accidentally scoped it too narrowly.
28 |
29 | 💰 Tip: since you're going to want to share the same color customisations for multiple core plugins, you can create a JS object with the colors to add, and re-use that object multiple times for various corePlugins 🤙
30 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.problem.broader-scopes/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.problem.broader-scopes/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_02.color-tokens_05.problem.broader-scopes",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.14.10",
18 | "@types/react": "^18.3.3",
19 | "@types/react-dom": "^18.3.0",
20 | "@typescript-eslint/eslint-plugin": "^7.16.0",
21 | "@typescript-eslint/parser": "^7.16.0",
22 | "@vitejs/plugin-react": "^4.3.1",
23 | "autoprefixer": "^10.4.19",
24 | "eslint": "^9.6.0",
25 | "eslint-plugin-react-hooks": "^4.6.2",
26 | "eslint-plugin-react-refresh": "^0.4.8",
27 | "postcss": "^8.4.39",
28 | "prettier": "^3.3.2",
29 | "prettier-plugin-tailwindcss": "^0.6.5",
30 | "tailwindcss": "^3.4.4",
31 | "typescript": "^5.5.3",
32 | "vite": "^5.3.3"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.problem.broader-scopes/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.problem.broader-scopes/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.problem.broader-scopes/src/App.tsx:
--------------------------------------------------------------------------------
1 | export default function ColorDesignTokens() {
2 | return (
3 |
4 |
5 |
6 |
7 | Special Deal
8 |
9 |
10 | Opportunity of a lifetime
11 |
12 |
13 | Lorem ipsum dolor sit, amet consectetur adipisicing elit. Voluptates
14 | sint dicta quas explicabo rem quisquam consectetur aperiam facere
15 | temporibus aut. Voluptatibus minus nesciunt qui voluptas fugiat
16 | voluptate repudiandae error asperiores.
17 |
18 |
19 |
23 | Learn more
24 |
25 |
26 |
27 |
28 |
29 | Conversion rate
30 |
31 |
32 | 9.86%
33 |
34 |
35 |
36 |
37 | customers
38 |
39 |
40 | 1M+
41 |
42 |
43 |
44 |
45 |
46 | )
47 | }
48 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.problem.broader-scopes/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.problem.broader-scopes/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.problem.broader-scopes/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.problem.broader-scopes/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import { type Config } from 'tailwindcss'
2 |
3 | const colors = {
4 | teal: '#00FFE1',
5 | purple: '#6200FF',
6 | grey: {
7 | 0: '#FFFFFF',
8 | 5: '#EBEBEB',
9 | 10: '#DADADA',
10 | 20: '#C2C2C2',
11 | 30: '#AAAAAA',
12 | 40: '#919191',
13 | 50: '#797979',
14 | 60: '#616161',
15 | 70: '#494949',
16 | 80: '#313131',
17 | 90: '#181818',
18 | 100: '#000000',
19 | },
20 | }
21 |
22 | /*
23 | 🐨 Abstract reusable object for `backgroundColors`,
24 | `borderColors` and `textColors` here.
25 | */
26 |
27 | export default {
28 | content: ['index.html', './src/**/*.{js,ts,jsx,tsx}'],
29 | theme: {
30 | colors: {
31 | inherit: 'inherit',
32 | transparent: 'transparent',
33 | current: 'currentColor',
34 | },
35 | extend: {
36 | /*
37 | 🐨 Update the config below to use the newly abstracted
38 | `backgroundColors`, `borderColors` and `textColors`
39 | objects, applied to multiple core plugins.
40 | */
41 | backgroundColor: {
42 | highlight: colors.teal,
43 | accent: colors.purple,
44 | neutral: {
45 | DEFAULT: colors.grey['0'],
46 | inverted: colors.grey['100'],
47 | },
48 | subtle: colors.grey['5'],
49 | bold: colors.grey['80'],
50 | },
51 | textColor: {
52 | copy: colors.grey['100'],
53 | subtle: colors.grey['60'],
54 | muted: colors.grey['40'],
55 | inverted: colors.grey['5'],
56 | },
57 | borderColor: {
58 | bold: colors.grey['60'],
59 | subtle: colors.grey['40'],
60 | muted: colors.grey['20'],
61 | },
62 | },
63 | },
64 | plugins: [],
65 | } satisfies Config
66 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.problem.broader-scopes/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.problem.broader-scopes/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.problem.broader-scopes/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.problem.broader-scopes/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react'
2 | import { defineConfig } from 'vite'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | server: {
8 | port: Number(process.env.PORT) || 3000,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.solution.broader-scopes/README.mdx:
--------------------------------------------------------------------------------
1 | # Broader Scopes
2 |
3 |
4 |
5 | Look at you go! Another head-scratching time-saver for your developers — or your future self 🤣
6 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.solution.broader-scopes/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.solution.broader-scopes/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_02.color-tokens_05.solution.broader-scopes",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.14.10",
18 | "@types/react": "^18.3.3",
19 | "@types/react-dom": "^18.3.0",
20 | "@typescript-eslint/eslint-plugin": "^7.16.0",
21 | "@typescript-eslint/parser": "^7.16.0",
22 | "@vitejs/plugin-react": "^4.3.1",
23 | "autoprefixer": "^10.4.19",
24 | "eslint": "^9.6.0",
25 | "eslint-plugin-react-hooks": "^4.6.2",
26 | "eslint-plugin-react-refresh": "^0.4.8",
27 | "postcss": "^8.4.39",
28 | "prettier": "^3.3.2",
29 | "prettier-plugin-tailwindcss": "^0.6.5",
30 | "tailwindcss": "^3.4.4",
31 | "typescript": "^5.5.3",
32 | "vite": "^5.3.3"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.solution.broader-scopes/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.solution.broader-scopes/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.solution.broader-scopes/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.solution.broader-scopes/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.solution.broader-scopes/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.solution.broader-scopes/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import { type Config } from 'tailwindcss'
2 |
3 | const colors = {
4 | teal: '#00FFE1',
5 | purple: '#6200FF',
6 | grey: {
7 | 0: '#FFFFFF',
8 | 5: '#EBEBEB',
9 | 10: '#DADADA',
10 | 20: '#C2C2C2',
11 | 30: '#AAAAAA',
12 | 40: '#919191',
13 | 50: '#797979',
14 | 60: '#616161',
15 | 70: '#494949',
16 | 80: '#313131',
17 | 90: '#181818',
18 | 100: '#000000',
19 | },
20 | }
21 |
22 | const backgroundColors = {
23 | highlight: colors.teal,
24 | accent: colors.purple,
25 | neutral: {
26 | DEFAULT: colors.grey['0'],
27 | inverted: colors.grey['100'],
28 | },
29 | subtle: colors.grey['5'],
30 | bold: colors.grey['80'],
31 | }
32 |
33 | const borderColors = {
34 | bold: colors.grey['60'],
35 | subtle: colors.grey['40'],
36 | muted: colors.grey['20'],
37 | }
38 |
39 | const textColors = {
40 | copy: colors.grey['100'],
41 | subtle: colors.grey['60'],
42 | muted: colors.grey['40'],
43 | inverted: colors.grey['5'],
44 | }
45 |
46 | export default {
47 | content: ['index.html', './src/**/*.{js,ts,jsx,tsx}'],
48 | theme: {
49 | colors: {
50 | inherit: 'inherit',
51 | transparent: 'transparent',
52 | current: 'currentColor',
53 | },
54 | extend: {
55 | // Background concerns
56 | backgroundColor: backgroundColors,
57 | gradientColorStops: backgroundColors,
58 | // ...
59 |
60 | // Border concerns
61 | borderColor: borderColors,
62 | stroke: borderColors,
63 | outlineColor: borderColors,
64 | ringColor: borderColors,
65 | // ...
66 |
67 | textColor: textColors,
68 | fill: textColors,
69 | // ...
70 | },
71 | },
72 | plugins: [],
73 | } satisfies Config
74 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.solution.broader-scopes/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.solution.broader-scopes/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.solution.broader-scopes/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/05.solution.broader-scopes/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react'
2 | import { defineConfig } from 'vite'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | server: {
8 | port: Number(process.env.PORT) || 3000,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.problem.css-variables/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.problem.css-variables/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_02.color-tokens_06.problem.css-variables",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.14.10",
18 | "@types/react": "^18.3.3",
19 | "@types/react-dom": "^18.3.0",
20 | "@typescript-eslint/eslint-plugin": "^7.16.0",
21 | "@typescript-eslint/parser": "^7.16.0",
22 | "@vitejs/plugin-react": "^4.3.1",
23 | "autoprefixer": "^10.4.19",
24 | "eslint": "^9.6.0",
25 | "eslint-plugin-react-hooks": "^4.6.2",
26 | "eslint-plugin-react-refresh": "^0.4.8",
27 | "postcss": "^8.4.39",
28 | "prettier": "^3.3.2",
29 | "prettier-plugin-tailwindcss": "^0.6.5",
30 | "tailwindcss": "^3.4.4",
31 | "typescript": "^5.5.3",
32 | "vite": "^5.3.3"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.problem.css-variables/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.problem.css-variables/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.problem.css-variables/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Demo } from './components/Demo'
2 |
3 | export default function ColorDesignTokens() {
4 | return (
5 | <>
6 |
7 | {/*
8 | 🐨 5. Add a second component and give it a style attribute
9 | that changes the value of the `--color-bg-subtle` CSS variable.
10 |
11 | You can try `0 0% 19%` for the value.
12 |
13 | The should render with a dark background!
14 | */}
15 | >
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.problem.css-variables/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | /*
6 | 🐨 2. Define the 14 colors as CSS variables at
7 | the `:root` scope, inside the `@base` layer.
8 | */
9 |
10 | /*
11 | 🐨 3. Recreate the semantic token abstractions as CSS variables.
12 | Those should consume the raw color CSS variables.
13 |
14 | Example: `--color-bg-neutral: var(--color-grey-0);`
15 | */
16 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.problem.css-variables/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.problem.css-variables/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.problem.css-variables/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.problem.css-variables/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.problem.css-variables/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.problem.css-variables/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react'
2 | import { defineConfig } from 'vite'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | server: {
8 | port: Number(process.env.PORT) || 3000,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.solution.css-variables/README.mdx:
--------------------------------------------------------------------------------
1 | # CSS Variables
2 |
3 |
4 |
5 | Fewwwwww.
6 |
7 | Congratulations — you've just done your first (but GIANT) step towards building support for **dark mode** and **multiple color themes** in your application!
8 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.solution.css-variables/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.solution.css-variables/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_02.color-tokens_06.solution.css-variables",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.14.10",
18 | "@types/react": "^18.3.3",
19 | "@types/react-dom": "^18.3.0",
20 | "@typescript-eslint/eslint-plugin": "^7.16.0",
21 | "@typescript-eslint/parser": "^7.16.0",
22 | "@vitejs/plugin-react": "^4.3.1",
23 | "autoprefixer": "^10.4.19",
24 | "eslint": "^9.6.0",
25 | "eslint-plugin-react-hooks": "^4.6.2",
26 | "eslint-plugin-react-refresh": "^0.4.8",
27 | "postcss": "^8.4.39",
28 | "prettier": "^3.3.2",
29 | "prettier-plugin-tailwindcss": "^0.6.5",
30 | "tailwindcss": "^3.4.4",
31 | "typescript": "^5.5.3",
32 | "vite": "^5.3.3"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.solution.css-variables/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.solution.css-variables/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.solution.css-variables/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Demo } from './components/Demo'
2 |
3 | export default function ColorDesignTokens() {
4 | return (
5 | <>
6 |
7 |
10 | >
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.solution.css-variables/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | :root {
7 | /* Raw color accessors */
8 | --color-teal: 173 100% 50%;
9 | --color-purple: 263 100% 50%;
10 |
11 | --color-grey-0: 0 0% 100%;
12 | --color-grey-5: 0 0% 92%;
13 | --color-grey-10: 0 0% 85%;
14 | --color-grey-20: 0 0% 76%;
15 | --color-grey-30: 0 0% 67%;
16 | --color-grey-40: 0 0% 57%;
17 | --color-grey-50: 0 0% 47%;
18 | --color-grey-60: 0 0% 38%;
19 | --color-grey-70: 0 0% 29%;
20 | --color-grey-80: 0 0% 19%;
21 | --color-grey-90: 0 0% 9%;
22 | --color-grey-100: 0 0% 0%;
23 |
24 | /* Semantic tokens */
25 | --color-bg-highlight: var(--color-teal);
26 | --color-bg-accent: var(--color-purple);
27 |
28 | --color-bg-neutral: var(--color-grey-0);
29 | --color-bg-neutral-inverted: var(--color-grey-100);
30 | --color-bg-subtle: var(--color-grey-5);
31 | --color-bg-bold: var(--color-grey-80);
32 |
33 | --color-border-bold: var(--color-grey-60);
34 | --color-border-subtle: var(--color-grey-40);
35 | --color-border-muted: var(--color-grey-20);
36 |
37 | --color-text-copy: var(--color-grey-100);
38 | --color-text-subtle: var(--color-grey-60);
39 | --color-text-muted: var(--color-grey-40);
40 | --color-text-inverted: var(--color-grey-5);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.solution.css-variables/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.solution.css-variables/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.solution.css-variables/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import { type Config } from 'tailwindcss'
2 |
3 | const backgroundColors = {
4 | highlight: 'hsl(var(--color-bg-highlight) / )',
5 | accent: 'hsl(var(--color-bg-accent) / )',
6 | neutral: {
7 | DEFAULT: 'hsl(var(--color-bg-neutral) / )',
8 | inverted: 'hsl(var(--color-bg-neutral-inverted) / )',
9 | },
10 | subtle: 'hsl(var(--color-bg-subtle) / )',
11 | bold: 'hsl(var(--color-bg-bold) / )',
12 | }
13 |
14 | const borderColors = {
15 | bold: 'hsl(var(--color-border-bold) / )',
16 | subtle: 'hsl(var(--color-border-subtle) / )',
17 | muted: 'hsl(var(--color-border-muted) / )',
18 | }
19 |
20 | const textColors = {
21 | copy: 'hsl(var(--color-text-copy) / )',
22 | subtle: 'hsl(var(--color-text-subtle) / )',
23 | muted: 'hsl(var(--color-text-muted) / )',
24 | inverted: 'hsl(var(--color-text-inverted) / )',
25 | }
26 |
27 | export default {
28 | content: ['index.html', './src/**/*.{js,ts,jsx,tsx}'],
29 | theme: {
30 | colors: {
31 | inherit: 'inherit',
32 | transparent: 'transparent',
33 | current: 'currentColor',
34 | },
35 | extend: {
36 | // Background concerns
37 | backgroundColor: backgroundColors,
38 | gradientColorStops: backgroundColors,
39 | // ...
40 |
41 | // Border concerns
42 | borderColor: borderColors,
43 | stroke: borderColors,
44 | outlineColor: borderColors,
45 | ringColor: borderColors,
46 | // ...
47 |
48 | textColor: textColors,
49 | fill: textColors,
50 | // ...
51 | },
52 | },
53 | plugins: [],
54 | } satisfies Config
55 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.solution.css-variables/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.solution.css-variables/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.solution.css-variables/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/06.solution.css-variables/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react'
2 | import { defineConfig } from 'vite'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | server: {
8 | port: Number(process.env.PORT) || 3000,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.problem.dark-mode/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.problem.dark-mode/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_02.color-tokens_07.problem.dark-mode",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.14.10",
18 | "@types/react": "^18.3.3",
19 | "@types/react-dom": "^18.3.0",
20 | "@typescript-eslint/eslint-plugin": "^7.16.0",
21 | "@typescript-eslint/parser": "^7.16.0",
22 | "@vitejs/plugin-react": "^4.3.1",
23 | "autoprefixer": "^10.4.19",
24 | "eslint": "^9.6.0",
25 | "eslint-plugin-react-hooks": "^4.6.2",
26 | "eslint-plugin-react-refresh": "^0.4.8",
27 | "postcss": "^8.4.39",
28 | "prettier": "^3.3.2",
29 | "prettier-plugin-tailwindcss": "^0.6.5",
30 | "tailwindcss": "^3.4.4",
31 | "typescript": "^5.5.3",
32 | "vite": "^5.3.3"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.problem.dark-mode/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.problem.dark-mode/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.problem.dark-mode/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Demo } from './components/Demo'
2 |
3 | export default function ColorDesignTokens() {
4 | return (
5 | <>
6 |
7 |
13 | >
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.problem.dark-mode/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | :root {
7 | /* Raw color accessors */
8 | --color-teal: 173 100% 50%;
9 | --color-purple: 263 100% 50%;
10 |
11 | --color-grey-0: 0 0% 100%;
12 | --color-grey-5: 0 0% 92%;
13 | --color-grey-10: 0 0% 85%;
14 | --color-grey-20: 0 0% 76%;
15 | --color-grey-30: 0 0% 67%;
16 | --color-grey-40: 0 0% 57%;
17 | --color-grey-50: 0 0% 47%;
18 | --color-grey-60: 0 0% 38%;
19 | --color-grey-70: 0 0% 29%;
20 | --color-grey-80: 0 0% 19%;
21 | --color-grey-90: 0 0% 9%;
22 | --color-grey-100: 0 0% 0%;
23 |
24 | /* Semantic tokens */
25 | --color-bg-highlight: var(--color-teal);
26 | --color-bg-accent: var(--color-purple);
27 |
28 | --color-bg-neutral: var(--color-grey-0);
29 | --color-bg-neutral-inverted: var(--color-grey-100);
30 | --color-bg-subtle: var(--color-grey-5);
31 | --color-bg-bold: var(--color-grey-80);
32 |
33 | --color-border-bold: var(--color-grey-60);
34 | --color-border-subtle: var(--color-grey-40);
35 | --color-border-muted: var(--color-grey-20);
36 |
37 | --color-text-copy: var(--color-grey-100);
38 | --color-text-subtle: var(--color-grey-60);
39 | --color-text-muted: var(--color-grey-40);
40 | --color-text-inverted: var(--color-grey-5);
41 | }
42 |
43 | /*
44 | 🐨 Redefine the semantic token values inside a `.dark` scope selector.
45 | */
46 | }
47 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.problem.dark-mode/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.problem.dark-mode/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.problem.dark-mode/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import { type Config } from 'tailwindcss'
2 |
3 | const backgroundColors = {
4 | highlight: 'hsl(var(--color-bg-highlight) / )',
5 | accent: 'hsl(var(--color-bg-accent) / )',
6 | neutral: {
7 | DEFAULT: 'hsl(var(--color-bg-neutral) / )',
8 | inverted: 'hsl(var(--color-bg-neutral-inverted) / )',
9 | },
10 | subtle: 'hsl(var(--color-bg-subtle) / )',
11 | bold: 'hsl(var(--color-bg-bold) / )',
12 | }
13 |
14 | const borderColors = {
15 | bold: 'hsl(var(--color-border-bold) / )',
16 | subtle: 'hsl(var(--color-border-subtle) / )',
17 | muted: 'hsl(var(--color-border-muted) / )',
18 | }
19 |
20 | const textColors = {
21 | copy: 'hsl(var(--color-text-copy) / )',
22 | subtle: 'hsl(var(--color-text-subtle) / )',
23 | muted: 'hsl(var(--color-text-muted) / )',
24 | inverted: 'hsl(var(--color-text-inverted) / )',
25 | }
26 |
27 | export default {
28 | content: ['index.html', './src/**/*.{js,ts,jsx,tsx}'],
29 | // 🐨 Add the `darkMode` key here, and set its value to 'class'
30 | theme: {
31 | colors: {
32 | inherit: 'inherit',
33 | transparent: 'transparent',
34 | current: 'currentColor',
35 | },
36 | extend: {
37 | // Background concerns
38 | backgroundColor: backgroundColors,
39 | gradientColorStops: backgroundColors,
40 | // ...
41 |
42 | // Border concerns
43 | borderColor: borderColors,
44 | stroke: borderColors,
45 | outlineColor: borderColors,
46 | ringColor: borderColors,
47 | // ...
48 |
49 | textColor: textColors,
50 | fill: textColors,
51 | // ...
52 | },
53 | },
54 | plugins: [],
55 | } satisfies Config
56 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.problem.dark-mode/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.problem.dark-mode/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.problem.dark-mode/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.problem.dark-mode/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react'
2 | import { defineConfig } from 'vite'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | server: {
8 | port: Number(process.env.PORT) || 3000,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.solution.dark-mode/README.mdx:
--------------------------------------------------------------------------------
1 | # Dark Mode
2 |
3 |
4 |
5 | Good work!
6 |
7 | You now know how to implement dark mode in any project.
8 |
9 | And what a difference dark mode support makes. It's like... night and day 🥁
10 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.solution.dark-mode/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.solution.dark-mode/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_02.color-tokens_07.solution.dark-mode",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.14.10",
18 | "@types/react": "^18.3.3",
19 | "@types/react-dom": "^18.3.0",
20 | "@typescript-eslint/eslint-plugin": "^7.16.0",
21 | "@typescript-eslint/parser": "^7.16.0",
22 | "@vitejs/plugin-react": "^4.3.1",
23 | "autoprefixer": "^10.4.19",
24 | "eslint": "^9.6.0",
25 | "eslint-plugin-react-hooks": "^4.6.2",
26 | "eslint-plugin-react-refresh": "^0.4.8",
27 | "postcss": "^8.4.39",
28 | "prettier": "^3.3.2",
29 | "prettier-plugin-tailwindcss": "^0.6.5",
30 | "tailwindcss": "^3.4.4",
31 | "typescript": "^5.5.3",
32 | "vite": "^5.3.3"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.solution.dark-mode/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.solution.dark-mode/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.solution.dark-mode/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Demo } from './components/Demo'
2 |
3 | export default function ColorDesignTokens() {
4 | return (
5 | <>
6 |
7 |
8 | >
9 | )
10 | }
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.solution.dark-mode/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.solution.dark-mode/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.solution.dark-mode/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import { type Config } from 'tailwindcss'
2 |
3 | const backgroundColors = {
4 | highlight: 'hsl(var(--color-bg-highlight) / )',
5 | accent: 'hsl(var(--color-bg-accent) / )',
6 | neutral: {
7 | DEFAULT: 'hsl(var(--color-bg-neutral) / )',
8 | inverted: 'hsl(var(--color-bg-neutral-inverted) / )',
9 | },
10 | subtle: 'hsl(var(--color-bg-subtle) / )',
11 | bold: 'hsl(var(--color-bg-bold) / )',
12 | }
13 |
14 | const borderColors = {
15 | bold: 'hsl(var(--color-border-bold) / )',
16 | subtle: 'hsl(var(--color-border-subtle) / )',
17 | muted: 'hsl(var(--color-border-muted) / )',
18 | }
19 |
20 | const textColors = {
21 | copy: 'hsl(var(--color-text-copy) / )',
22 | subtle: 'hsl(var(--color-text-subtle) / )',
23 | muted: 'hsl(var(--color-text-muted) / )',
24 | inverted: 'hsl(var(--color-text-inverted) / )',
25 | }
26 |
27 | export default {
28 | content: ['index.html', './src/**/*.{js,ts,jsx,tsx}'],
29 | darkMode: 'class',
30 | theme: {
31 | colors: {
32 | inherit: 'inherit',
33 | transparent: 'transparent',
34 | current: 'currentColor',
35 | },
36 | extend: {
37 | // Background concerns
38 | backgroundColor: backgroundColors,
39 | gradientColorStops: backgroundColors,
40 | // ...
41 |
42 | // Border concerns
43 | borderColor: borderColors,
44 | stroke: borderColors,
45 | outlineColor: borderColors,
46 | ringColor: borderColors,
47 | // ...
48 |
49 | textColor: textColors,
50 | fill: textColors,
51 | // ...
52 | },
53 | },
54 | plugins: [],
55 | } satisfies Config
56 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.solution.dark-mode/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.solution.dark-mode/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.solution.dark-mode/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/07.solution.dark-mode/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react'
2 | import { defineConfig } from 'vite'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | server: {
8 | port: Number(process.env.PORT) || 3000,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.problem.multi-theme/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.problem.multi-theme/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_02.color-tokens_08.problem.multi-theme",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.14.10",
18 | "@types/react": "^18.3.3",
19 | "@types/react-dom": "^18.3.0",
20 | "@typescript-eslint/eslint-plugin": "^7.16.0",
21 | "@typescript-eslint/parser": "^7.16.0",
22 | "@vitejs/plugin-react": "^4.3.1",
23 | "autoprefixer": "^10.4.19",
24 | "eslint": "^9.6.0",
25 | "eslint-plugin-react-hooks": "^4.6.2",
26 | "eslint-plugin-react-refresh": "^0.4.8",
27 | "postcss": "^8.4.39",
28 | "prettier": "^3.3.2",
29 | "prettier-plugin-tailwindcss": "^0.6.5",
30 | "tailwindcss": "^3.4.4",
31 | "typescript": "^5.5.3",
32 | "vite": "^5.3.3"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.problem.multi-theme/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.problem.multi-theme/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.problem.multi-theme/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Demo } from './components/Demo'
2 |
3 | export default function ColorDesignTokens() {
4 | return (
5 | <>
6 |
7 |
8 | {/*
9 | 🐨 Add a couple of components to test the
10 | "citrus" theme in both light and dark modes.
11 | */}
12 | >
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.problem.multi-theme/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.problem.multi-theme/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.problem.multi-theme/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import { type Config } from 'tailwindcss'
2 |
3 | const backgroundColors = {
4 | highlight: 'hsl(var(--color-bg-highlight) / )',
5 | accent: 'hsl(var(--color-bg-accent) / )',
6 | neutral: {
7 | DEFAULT: 'hsl(var(--color-bg-neutral) / )',
8 | inverted: 'hsl(var(--color-bg-neutral-inverted) / )',
9 | },
10 | subtle: 'hsl(var(--color-bg-subtle) / )',
11 | bold: 'hsl(var(--color-bg-bold) / )',
12 | }
13 |
14 | const borderColors = {
15 | bold: 'hsl(var(--color-border-bold) / )',
16 | subtle: 'hsl(var(--color-border-subtle) / )',
17 | muted: 'hsl(var(--color-border-muted) / )',
18 | }
19 |
20 | const textColors = {
21 | copy: 'hsl(var(--color-text-copy) / )',
22 | subtle: 'hsl(var(--color-text-subtle) / )',
23 | muted: 'hsl(var(--color-text-muted) / )',
24 | inverted: 'hsl(var(--color-text-inverted) / )',
25 | }
26 |
27 | export default {
28 | content: ['index.html', './src/**/*.{js,ts,jsx,tsx}'],
29 | darkMode: 'class',
30 | theme: {
31 | colors: {
32 | inherit: 'inherit',
33 | transparent: 'transparent',
34 | current: 'currentColor',
35 | },
36 | extend: {
37 | // Background concerns
38 | backgroundColor: backgroundColors,
39 | gradientColorStops: backgroundColors,
40 | // ...
41 |
42 | // Border concerns
43 | borderColor: borderColors,
44 | stroke: borderColors,
45 | outlineColor: borderColors,
46 | ringColor: borderColors,
47 | // ...
48 |
49 | textColor: textColors,
50 | fill: textColors,
51 | // ...
52 | },
53 | },
54 | plugins: [],
55 | } satisfies Config
56 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.problem.multi-theme/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.problem.multi-theme/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.problem.multi-theme/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.problem.multi-theme/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react'
2 | import { defineConfig } from 'vite'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | server: {
8 | port: Number(process.env.PORT) || 3000,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.solution.multi-theme/README.mdx:
--------------------------------------------------------------------------------
1 | # Multi-Theme Support
2 |
3 |
4 |
5 | Nicely done — You're an absolute legend!
6 |
7 | With this technique, you can create 3, 5 or even 100 different, dark-mode-capable color themes for your project 🤯
8 |
9 | That's pretty cool, **seriously**.
10 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.solution.multi-theme/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.solution.multi-theme/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_02.color-tokens_08.solution.multi-theme",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.14.10",
18 | "@types/react": "^18.3.3",
19 | "@types/react-dom": "^18.3.0",
20 | "@typescript-eslint/eslint-plugin": "^7.16.0",
21 | "@typescript-eslint/parser": "^7.16.0",
22 | "@vitejs/plugin-react": "^4.3.1",
23 | "autoprefixer": "^10.4.19",
24 | "eslint": "^9.6.0",
25 | "eslint-plugin-react-hooks": "^4.6.2",
26 | "eslint-plugin-react-refresh": "^0.4.8",
27 | "postcss": "^8.4.39",
28 | "prettier": "^3.3.2",
29 | "prettier-plugin-tailwindcss": "^0.6.5",
30 | "tailwindcss": "^3.4.4",
31 | "typescript": "^5.5.3",
32 | "vite": "^5.3.3"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.solution.multi-theme/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.solution.multi-theme/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.solution.multi-theme/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Demo } from './components/Demo'
2 |
3 | export default function ColorDesignTokens() {
4 | return (
5 | <>
6 |
7 |
8 |
9 |
10 | >
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.solution.multi-theme/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.solution.multi-theme/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.solution.multi-theme/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import { type Config } from 'tailwindcss'
2 |
3 | const backgroundColors = {
4 | highlight: 'hsl(var(--color-bg-highlight) / )',
5 | accent: 'hsl(var(--color-bg-accent) / )',
6 | neutral: {
7 | DEFAULT: 'hsl(var(--color-bg-neutral) / )',
8 | inverted: 'hsl(var(--color-bg-neutral-inverted) / )',
9 | },
10 | subtle: 'hsl(var(--color-bg-subtle) / )',
11 | bold: 'hsl(var(--color-bg-bold) / )',
12 | }
13 |
14 | const borderColors = {
15 | bold: 'hsl(var(--color-border-bold) / )',
16 | subtle: 'hsl(var(--color-border-subtle) / )',
17 | muted: 'hsl(var(--color-border-muted) / )',
18 | }
19 |
20 | const textColors = {
21 | copy: 'hsl(var(--color-text-copy) / )',
22 | subtle: 'hsl(var(--color-text-subtle) / )',
23 | muted: 'hsl(var(--color-text-muted) / )',
24 | inverted: 'hsl(var(--color-text-inverted) / )',
25 | }
26 |
27 | export default {
28 | content: ['index.html', './src/**/*.{js,ts,jsx,tsx}'],
29 | darkMode: 'class',
30 | theme: {
31 | colors: {
32 | inherit: 'inherit',
33 | transparent: 'transparent',
34 | current: 'currentColor',
35 | },
36 | extend: {
37 | // Background concerns
38 | backgroundColor: backgroundColors,
39 | gradientColorStops: backgroundColors,
40 | // ...
41 |
42 | // Border concerns
43 | borderColor: borderColors,
44 | stroke: borderColors,
45 | outlineColor: borderColors,
46 | ringColor: borderColors,
47 | // ...
48 |
49 | textColor: textColors,
50 | fill: textColors,
51 | // ...
52 | },
53 | },
54 | plugins: [],
55 | } satisfies Config
56 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.solution.multi-theme/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.solution.multi-theme/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.solution.multi-theme/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/08.solution.multi-theme/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react'
2 | import { defineConfig } from 'vite'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | server: {
8 | port: Number(process.env.PORT) || 3000,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/FINISHED.mdx:
--------------------------------------------------------------------------------
1 | # Color Design Tokens
2 |
3 |
4 |
5 | Hot damn, this was a big segment.
6 |
7 | But I'm pretty sure that the value it unlocks made it worth it!
8 |
9 | ## Tell me what you think!
10 |
11 | If you could spend 2 mintues filling the feedback form — that truly helps make the workshop materials better for everyone ❤️
12 |
13 | ## Break time
14 |
15 | Make sure you take a good break before jumping into the next exercise.
16 |
--------------------------------------------------------------------------------
/exercises/02.color-tokens/README.mdx:
--------------------------------------------------------------------------------
1 | # Color Design Tokens
2 |
3 |
4 |
5 | In this exercise, we'll implement the custom colors in our Tailwind CSS project.
6 |
7 | We'll start with the "raw" colors, and then add a layer of abstraction for semantic color tokens.
8 |
9 |
13 |
14 | Ready? You know what to do!
15 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.problem.theme-config-in-css/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.problem.theme-config-in-css/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_03.tailwind-v4_01.problem.theme-config-in-css",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@tailwindcss/vite": "^4.0.0-alpha.17",
18 | "@types/node": "^20.14.10",
19 | "@types/react": "^18.3.3",
20 | "@types/react-dom": "^18.3.0",
21 | "@typescript-eslint/eslint-plugin": "^7.16.0",
22 | "@typescript-eslint/parser": "^7.16.0",
23 | "@vitejs/plugin-react": "^4.3.1",
24 | "autoprefixer": "^10.4.19",
25 | "eslint": "^9.6.0",
26 | "eslint-plugin-react-hooks": "^4.6.2",
27 | "eslint-plugin-react-refresh": "^0.4.8",
28 | "postcss": "^8.4.39",
29 | "prettier": "^3.3.2",
30 | "prettier-plugin-tailwindcss": "^0.6.5",
31 | "tailwindcss": "^4.0.0-alpha.17",
32 | "typescript": "^5.5.3",
33 | "vite": "^5.3.3"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.problem.theme-config-in-css/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.problem.theme-config-in-css/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Demo } from './components/Demo'
2 |
3 | export default function ColorDesignTokens() {
4 | return (
5 | <>
6 |
7 |
8 |
9 |
10 | >
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.problem.theme-config-in-css/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.problem.theme-config-in-css/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.problem.theme-config-in-css/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | const backgroundColors = {
2 | highlight: 'hsl(var(--color-bg-highlight) / )',
3 | accent: 'hsl(var(--color-bg-accent) / )',
4 | neutral: {
5 | DEFAULT: 'hsl(var(--color-bg-neutral) / )',
6 | inverted: 'hsl(var(--color-bg-neutral-inverted) / )',
7 | },
8 | subtle: 'hsl(var(--color-bg-subtle) / )',
9 | bold: 'hsl(var(--color-bg-bold) / )',
10 | }
11 |
12 | const borderColors = {
13 | bold: 'hsl(var(--color-border-bold) / )',
14 | subtle: 'hsl(var(--color-border-subtle) / )',
15 | muted: 'hsl(var(--color-border-muted) / )',
16 | }
17 |
18 | const textColors = {
19 | copy: 'hsl(var(--color-text-copy) / )',
20 | subtle: 'hsl(var(--color-text-subtle) / )',
21 | muted: 'hsl(var(--color-text-muted) / )',
22 | inverted: 'hsl(var(--color-text-inverted) / )',
23 | }
24 |
25 | export default {
26 | content: ['index.html', './src/**/*.{js,ts,jsx,tsx}'],
27 | darkMode: 'class',
28 | theme: {
29 | colors: {
30 | inherit: 'inherit',
31 | transparent: 'transparent',
32 | current: 'currentColor',
33 | },
34 | extend: {
35 | // Background concerns
36 | backgroundColor: backgroundColors,
37 | gradientColorStops: backgroundColors,
38 | // ...
39 |
40 | // Border concerns
41 | borderColor: borderColors,
42 | stroke: borderColors,
43 | outlineColor: borderColors,
44 | ringColor: borderColors,
45 | // ...
46 |
47 | textColor: textColors,
48 | fill: textColors,
49 | // ...
50 | },
51 | },
52 | plugins: [],
53 | }
54 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.problem.theme-config-in-css/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.problem.theme-config-in-css/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.problem.theme-config-in-css/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.problem.theme-config-in-css/vite.config.ts:
--------------------------------------------------------------------------------
1 | import tailwindVite from '@tailwindcss/vite'
2 | import react from '@vitejs/plugin-react'
3 | import { defineConfig } from 'vite'
4 |
5 | // https://vitejs.dev/config/
6 | export default defineConfig({
7 | plugins: [react(), tailwindVite()],
8 | server: {
9 | port: Number(process.env.PORT) || 3000,
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.solution.theme-config-in-css/README.mdx:
--------------------------------------------------------------------------------
1 | # Theme Configuration in CSS
2 |
3 |
4 |
5 | Sweet — that was a lot simpler, wasn't it?
6 |
7 | ## Wait. We're not done yet.
8 |
9 | In the final step, we'll make things aggressively more simple.
10 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.solution.theme-config-in-css/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.solution.theme-config-in-css/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_03.tailwind-v4_01.solution.theme-config-in-css",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@tailwindcss/vite": "^4.0.0-alpha.17",
18 | "@types/node": "^20.14.10",
19 | "@types/react": "^18.3.3",
20 | "@types/react-dom": "^18.3.0",
21 | "@typescript-eslint/eslint-plugin": "^7.16.0",
22 | "@typescript-eslint/parser": "^7.16.0",
23 | "@vitejs/plugin-react": "^4.3.1",
24 | "autoprefixer": "^10.4.19",
25 | "eslint": "^9.6.0",
26 | "eslint-plugin-react-hooks": "^4.6.2",
27 | "eslint-plugin-react-refresh": "^0.4.8",
28 | "postcss": "^8.4.39",
29 | "prettier": "^3.3.2",
30 | "prettier-plugin-tailwindcss": "^0.6.5",
31 | "tailwindcss": "^4.0.0-alpha.17",
32 | "typescript": "^5.5.3",
33 | "vite": "^5.3.3"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.solution.theme-config-in-css/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.solution.theme-config-in-css/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Demo } from './components/Demo'
2 |
3 | export default function ColorDesignTokens() {
4 | return (
5 | <>
6 |
7 |
8 |
9 |
10 | >
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.solution.theme-config-in-css/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.solution.theme-config-in-css/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.solution.theme-config-in-css/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.solution.theme-config-in-css/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.solution.theme-config-in-css/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/01.solution.theme-config-in-css/vite.config.ts:
--------------------------------------------------------------------------------
1 | import tailwindVite from '@tailwindcss/vite'
2 | import react from '@vitejs/plugin-react'
3 | import { defineConfig } from 'vite'
4 |
5 | // https://vitejs.dev/config/
6 | export default defineConfig({
7 | plugins: [react(), tailwindVite()],
8 | server: {
9 | port: Number(process.env.PORT) || 3000,
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.problem.simplification/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.problem.simplification/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_03.tailwind-v4_02.problem.simplification",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@tailwindcss/vite": "^4.0.0-alpha.17",
18 | "@types/node": "^20.14.10",
19 | "@types/react": "^18.3.3",
20 | "@types/react-dom": "^18.3.0",
21 | "@typescript-eslint/eslint-plugin": "^7.16.0",
22 | "@typescript-eslint/parser": "^7.16.0",
23 | "@vitejs/plugin-react": "^4.3.1",
24 | "autoprefixer": "^10.4.19",
25 | "eslint": "^9.6.0",
26 | "eslint-plugin-react-hooks": "^4.6.2",
27 | "eslint-plugin-react-refresh": "^0.4.8",
28 | "postcss": "^8.4.39",
29 | "prettier": "^3.3.2",
30 | "prettier-plugin-tailwindcss": "^0.6.5",
31 | "tailwindcss": "^4.0.0-alpha.17",
32 | "typescript": "^5.5.3",
33 | "vite": "^5.3.3"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.problem.simplification/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.problem.simplification/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Demo } from './components/Demo'
2 |
3 | export default function ColorDesignTokens() {
4 | return (
5 | <>
6 |
7 |
8 |
9 |
10 | >
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.problem.simplification/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.problem.simplification/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.problem.simplification/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.problem.simplification/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.problem.simplification/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.problem.simplification/vite.config.ts:
--------------------------------------------------------------------------------
1 | import tailwindVite from '@tailwindcss/vite'
2 | import react from '@vitejs/plugin-react'
3 | import { defineConfig } from 'vite'
4 |
5 | // https://vitejs.dev/config/
6 | export default defineConfig({
7 | plugins: [react(), tailwindVite()],
8 | server: {
9 | port: Number(process.env.PORT) || 3000,
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.solution.simplification/README.mdx:
--------------------------------------------------------------------------------
1 | # Simplification
2 |
3 |
4 | ## Ummm, that was annoying to do
5 |
6 | Yep — I don't disagree. But here's something to lift up your mood again:
7 |
8 | ### We has color swatches again!
9 |
10 | Because we're using color values directly and not referencing another CSS variable now, the Tailwind Intellisense is able to work out what actual color is being held in utility classes.
11 |
12 | And we get our nice little color swatches back in the editor!
13 |
14 |
15 |
16 | ### Swatches are not theme-aware
17 |
18 | Technically, it will always display the color defined in the `:root` scope (`@theme`).
19 |
20 | But still — this is pretty neat and way better than nothing!
21 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.solution.simplification/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.solution.simplification/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "exercises_03.tailwind-v4_02.solution.simplification",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "@tailwindcss/vite": "^4.0.0-alpha.17",
18 | "@types/node": "^20.14.10",
19 | "@types/react": "^18.3.3",
20 | "@types/react-dom": "^18.3.0",
21 | "@typescript-eslint/eslint-plugin": "^7.16.0",
22 | "@typescript-eslint/parser": "^7.16.0",
23 | "@vitejs/plugin-react": "^4.3.1",
24 | "autoprefixer": "^10.4.19",
25 | "eslint": "^9.6.0",
26 | "eslint-plugin-react-hooks": "^4.6.2",
27 | "eslint-plugin-react-refresh": "^0.4.8",
28 | "postcss": "^8.4.39",
29 | "prettier": "^3.3.2",
30 | "prettier-plugin-tailwindcss": "^0.6.5",
31 | "tailwindcss": "^4.0.0-alpha.17",
32 | "typescript": "^5.5.3",
33 | "vite": "^5.3.3"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.solution.simplification/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.solution.simplification/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Demo } from './components/Demo'
2 |
3 | export default function ColorDesignTokens() {
4 | return (
5 | <>
6 |
7 |
8 |
9 |
10 | >
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.solution.simplification/src/index.css:
--------------------------------------------------------------------------------
1 | @import 'tailwindcss';
2 |
3 | @theme {
4 | --color-*: initial;
5 |
6 | --background-color-highlight: #00ffe1;
7 | --background-color-accent: #6200ff;
8 |
9 | --background-color-neutral: #ffffff;
10 | --background-color-neutral-inverted: #000000;
11 | --background-color-subtle: #ebebeb;
12 | --background-color-bold: #303030;
13 |
14 | --border-color-bold: #616161;
15 | --border-color-subtle: #919191;
16 | --border-color-muted: #c2c2c2;
17 |
18 | --text-color-copy: #000000;
19 | --text-color-subtle: #616161;
20 | --text-color-muted: #919191;
21 | --text-color-inverted: #ebebeb;
22 | }
23 |
24 | @layer base {
25 | .dark {
26 | --background-color-highlight: #009987;
27 | --background-color-accent: #a166ff;
28 |
29 | --background-color-neutral: #000000;
30 | --background-color-neutral-inverted: #ffffff;
31 | --background-color-subtle: #313131;
32 | --background-color-bold: #ebebeb;
33 |
34 | --border-color-bold: #919191;
35 | --border-color-subtle: #616161;
36 | --border-color-muted: #313131;
37 |
38 | --text-color-copy: #ffffff;
39 | --text-color-subtle: #919191;
40 | --text-color-muted: #616161;
41 | --text-color-inverted: #181818;
42 | }
43 |
44 | [data-theme='citrus'] {
45 | --background-color-highlight: #ffff00;
46 | --background-color-accent: #ff8000;
47 | }
48 |
49 | [data-theme='citrus'].dark {
50 | --background-color-highlight: #999900;
51 | --background-color-accent: #ffb366;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.solution.simplification/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.solution.simplification/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.solution.simplification/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.solution.simplification/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.solution.simplification/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/02.solution.simplification/vite.config.ts:
--------------------------------------------------------------------------------
1 | import tailwindVite from '@tailwindcss/vite'
2 | import react from '@vitejs/plugin-react'
3 | import { defineConfig } from 'vite'
4 |
5 | // https://vitejs.dev/config/
6 | export default defineConfig({
7 | plugins: [react(), tailwindVite()],
8 | server: {
9 | port: Number(process.env.PORT) || 3000,
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/FINISHED.mdx:
--------------------------------------------------------------------------------
1 | # Conversion to Tailwind v4
2 |
3 |
4 |
5 | That's it — you are **done**!
6 |
7 | ## Dark mode & theming, in Tailwind v3 & v4
8 |
9 | You now know how to setup multiple themes with dark/light support in both Tailwind CSS v3 and v4 🎉
10 |
11 | That's pretty cool — congratulations!
12 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/README.mdx:
--------------------------------------------------------------------------------
1 | # Conversion to Tailwind v4
2 |
3 |
4 |
5 | Tailwind CSS v4 is coming!
6 |
7 | Or maybe it's _already_ here by the time you're reading this.
8 |
9 | Either way, in this final exercise for this workshop, we're goint to port over our work to the Tailwind v4 [CSS-first theme configuration](https://tailwindcss.com/blog/tailwindcss-v4-alpha#css-first-configuration).
10 |
--------------------------------------------------------------------------------
/exercises/03.tailwind-v4/instructor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epicweb-dev/tailwindcss-color-tokens/740f49c893ee982ae84a0d11ab4c4663419e93ed/exercises/03.tailwind-v4/instructor.png
--------------------------------------------------------------------------------
/exercises/FINISHED.mdx:
--------------------------------------------------------------------------------
1 | # Tailwind CSS Color Tokens 🎨
2 |
3 |
4 |
5 | Hooray! You're all done! 👏👏
6 |
7 | I'd really appreciate if you can take some time to complete the full workshop feedback form.
8 |
9 | That lets me know how I can improve the learning materials for everyone going forward ❤️
10 |
--------------------------------------------------------------------------------
/public/images/color-swatches-vscode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epicweb-dev/tailwindcss-color-tokens/740f49c893ee982ae84a0d11ab4c4663419e93ed/public/images/color-swatches-vscode.png
--------------------------------------------------------------------------------
/public/images/color-tokens-usage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epicweb-dev/tailwindcss-color-tokens/740f49c893ee982ae84a0d11ab4c4663419e93ed/public/images/color-tokens-usage.png
--------------------------------------------------------------------------------
/public/images/dark-mode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epicweb-dev/tailwindcss-color-tokens/740f49c893ee982ae84a0d11ab4c4663419e93ed/public/images/dark-mode.png
--------------------------------------------------------------------------------
/public/images/devmode-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epicweb-dev/tailwindcss-color-tokens/740f49c893ee982ae84a0d11ab4c4663419e93ed/public/images/devmode-2.png
--------------------------------------------------------------------------------
/public/images/devmode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epicweb-dev/tailwindcss-color-tokens/740f49c893ee982ae84a0d11ab4c4663419e93ed/public/images/devmode.png
--------------------------------------------------------------------------------
/public/images/highlight-text-eww.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epicweb-dev/tailwindcss-color-tokens/740f49c893ee982ae84a0d11ab4c4663419e93ed/public/images/highlight-text-eww.png
--------------------------------------------------------------------------------
/public/images/instructor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epicweb-dev/tailwindcss-color-tokens/740f49c893ee982ae84a0d11ab4c4663419e93ed/public/images/instructor.png
--------------------------------------------------------------------------------
/public/images/raw-color-tokens.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epicweb-dev/tailwindcss-color-tokens/740f49c893ee982ae84a0d11ab4c4663419e93ed/public/images/raw-color-tokens.png
--------------------------------------------------------------------------------
/public/images/semantic-color-tokens.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epicweb-dev/tailwindcss-color-tokens/740f49c893ee982ae84a0d11ab4c4663419e93ed/public/images/semantic-color-tokens.png
--------------------------------------------------------------------------------
/public/images/token-layers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epicweb-dev/tailwindcss-color-tokens/740f49c893ee982ae84a0d11ab4c4663419e93ed/public/images/token-layers.png
--------------------------------------------------------------------------------