├── .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 | screenshot of Figma's variables panel showing semantic color design tokens 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 --------------------------------------------------------------------------------