From 90b0b31e75d529f1892fbcd4d3cf0df7e80cee87 Mon Sep 17 00:00:00 2001 From: Andy Hook Date: Tue, 1 Jul 2025 19:25:20 +0100 Subject: [PATCH] V2 --- .babelrc | 4 - .eslintrc.json | 46 +- .gitignore | 5 +- .prettierrc | 4 - LICENSE | 2 +- README.md | 46 +- app/(index)/experience-list.tsx | 364 + app/(index)/experience.tsx | 99 + app/(index)/page.tsx | 22 + app/(index)/testimonials.tsx | 275 + app/(index)/work.tsx | 244 + .../_components/content-section.tsx | 51 + .../_components/content/aragon.tsx | 108 + .../_components/content/blocks.tsx | 73 + app/[projectId]/_components/content/dash.tsx | 102 + app/[projectId]/_components/content/radix.tsx | 3 + .../_components/image-group-section.tsx | 101 + app/[projectId]/_components/image-section.tsx | 49 + app/[projectId]/_components/team-list.tsx | 147 + app/[projectId]/_components/tooltip-link.tsx | 43 + app/[projectId]/page.tsx | 299 + app/favicon.ico | Bin 0 -> 15086 bytes app/globals.css | 7 + app/layout.tsx | 339 + app/opengraph-image.jpg | Bin 0 -> 174673 bytes app/router.tsx | 444 ++ app/sidebar.tsx | 520 ++ app/theme.tsx | 45 + components/About/About.tsx | 95 - components/AccessibleIcon/AccessibleIcon.tsx | 44 - components/Button/Button.tsx | 143 - components/ExperienceList/ExperienceList.tsx | 233 - components/Footer/Footer.tsx | 114 - components/GradientText/GradientText.tsx | 48 - components/Icon/Icon.tsx | 34 - components/ImageBase/ImageBase.tsx | 205 - .../InteractionBase/InteractionBase.spec.tsx | 156 - .../InteractionBase/InteractionBase.tsx | 178 - components/Layout/LayoutGutter.tsx | 37 - components/Layout/LayoutLimiter.tsx | 45 - components/Layout/LayoutRow.tsx | 61 - components/Layout/LayoutShade.tsx | 42 - components/Link/Link.tsx | 32 - .../LoadingIndicator/LoadingIndicator.tsx | 86 - components/Logo/Logo.tsx | 46 - components/Menu/Menu.tsx | 104 - components/Menu/MenuPanel.tsx | 390 - components/Menu/MenuTrigger.tsx | 86 - components/Meta/MetaBrowser.tsx | 26 - components/Meta/MetaIcons.tsx | 43 - components/Meta/MetaSocial.tsx | 61 - components/MoreProjects/MoreProjects.tsx | 119 - components/Panel/Panel.tsx | 29 - components/Pip/Pip.tsx | 28 - components/Project/ProjectDescription.tsx | 71 - components/Project/ProjectImage.tsx | 19 - components/Project/ProjectImageGroup.tsx | 164 - components/Project/ProjectQuote.tsx | 125 - components/ProjectCard/ProjectCard.tsx | 235 - .../ProjectTemplate/ProjectTemplate.tsx | 223 - components/QuoteCard/QuoteCard.tsx | 174 - components/RemoveWidow/RemoveWidow.tsx | 12 - components/ScrollReveal/ScrollReveal.tsx | 36 - components/Signature/Signature.tsx | 60 - components/SocialIcons/SocialIcons.tsx | 131 - components/SocialProof/SocialProof.tsx | 127 - .../StripeBackground/StripeBackground.tsx | 263 - components/Text/TextBase.tsx | 67 - components/Text/TextHeading.tsx | 30 - components/Text/TextParagraph.tsx | 34 - components/TopBar/TopBar.tsx | 141 - components/author.tsx | 75 + components/container.tsx | 31 + components/copy.tsx | 137 + components/download-button.tsx | 105 + components/external-favicon.tsx | 54 + components/external-link.tsx | 30 + components/focus-ring.tsx | 33 + components/gutter.tsx | 38 + components/hatch.tsx | 56 + components/icons/dribbble-icon.tsx | 17 + components/icons/github-icon.tsx | 17 + components/icons/instagram-icon.tsx | 17 + components/icons/linkedin-icon.tsx | 17 + components/icons/twitter-icon.tsx | 17 + components/line.tsx | 67 + components/logos/aragon-mark.tsx | 86 + components/logos/brandwatch-mark.tsx | 52 + components/logos/bright-mark.tsx | 39 + components/logos/freelance-mark.tsx | 43 + components/logos/modulz-mark.tsx | 22 + components/logos/scroll-mark.tsx | 35 + components/logos/tjc-mark.tsx | 51 + components/logos/workos-mark.tsx | 59 + components/primitives/accordion.tsx | 259 + components/primitives/collapsible.tsx | 134 + components/primitives/collection.tsx | 118 + components/primitives/hover-group.tsx | 99 + components/primitives/mouse-hover.tsx | 54 + components/primitives/primitive.tsx | 46 + components/primitives/scroll-area.tsx | 486 ++ components/primitives/slot.tsx | 89 + components/primitives/tooltip.tsx | 223 + components/quote.tsx | 21 + components/scramble-text.tsx | 83 + components/social-link.tsx | 77 + components/spinner.tsx | 106 + components/tooltip.tsx | 79 + components/utils/compose-refs.tsx | 34 + components/utils/create-context.tsx | 83 + components/utils/use-callback-ref.tsx | 16 + components/utils/use-controllable-state.tsx | 57 + components/utils/use-device.tsx | 30 + components/utils/use-layout-effect.tsx | 4 + components/utils/use-screen.tsx | 37 + components/work-item.tsx | 198 + cva.config.ts | 9 + data.ts | 443 ++ data/icons.ts | 27 - data/images.ts | 10 - data/meta.ts | 54 - data/projects.ts | 201 - data/recommendations.ts | 80 - generate-sizes.js | 32 - .../useDetectOutsideClick.tsx | 42 - .../useFocusVisible/useFocusVisible.spec.tsx | 68 - hooks/useFocusVisible/useFocusVisible.tsx | 159 - .../useLoadPercentage.spec.tsx | 72 - hooks/useLoadPercentage/useLoadPercentage.tsx | 94 - hooks/useLocationState/useLocationState.tsx | 39 - .../getRelativeMotionProps.ts | 22 - .../useRelativeYMotion.spec.tsx | 60 - .../useRelativeYMotion/useRelativeYMotion.tsx | 18 - hooks/useScrollPosition/useScrollPosition.tsx | 142 - hooks/useTheme/useTheme.tsx | 95 - images.ts | 408 ++ jest.setup.ts | 13 - next-env.d.ts | 5 +- next.config.js | 5 - next.config.mjs | 14 + package.json | 92 +- pages/_app.tsx | 69 - pages/_document.tsx | 53 - pages/aragon.tsx | 143 - pages/blocks.tsx | 112 - pages/bright.tsx | 133 - pages/index.tsx | 298 - pnpm-lock.yaml | 3707 ++++++++++ postcss.config.mjs | 9 + ...pdf => andy-hook-uk-software-engineer.pdf} | Bin public/favicon.ico | Bin 109229 -> 0 bytes public/icons/icon-144x144.png | Bin 21092 -> 0 bytes public/icons/icon-192x192.png | Bin 34928 -> 0 bytes public/icons/icon-256x256.png | Bin 57672 -> 0 bytes public/icons/icon-32x32.png | Bin 2096 -> 0 bytes public/icons/icon-384x384.png | Bin 119869 -> 0 bytes public/icons/icon-48x48.png | Bin 3728 -> 0 bytes public/icons/icon-512x512.png | Bin 204269 -> 0 bytes public/icons/icon-72x72.png | Bin 6898 -> 0 bytes public/icons/icon-96x96.png | Bin 10752 -> 0 bytes public/images/aragon-thumb-large.jpg | Bin 0 -> 1342058 bytes public/images/aragon-thumb.jpg | Bin 934310 -> 0 bytes public/images/avatars/adri-garcia.png | Bin 0 -> 475704 bytes public/images/avatars/alastair-bird.png | Bin 0 -> 663477 bytes public/images/avatars/andrew-khan.png | Bin 0 -> 374250 bytes public/images/avatars/andrew.jpg | Bin 29882 -> 0 bytes public/images/avatars/ben-browning.png | Bin 0 -> 333124 bytes public/images/avatars/ben.jpg | Bin 40952 -> 0 bytes public/images/avatars/benoit-grelard.png | Bin 0 -> 360142 bytes public/images/avatars/brett-sun.png | Bin 0 -> 132563 bytes public/images/avatars/brett.jpg | Bin 38720 -> 0 bytes public/images/avatars/jenna-smith.png | Bin 0 -> 315223 bytes public/images/avatars/jo.jpg | Bin 42130 -> 0 bytes public/images/avatars/michael-allan.png | Bin 0 -> 163518 bytes public/images/avatars/michael.jpg | Bin 36273 -> 0 bytes public/images/avatars/paty-davila.png | Bin 0 -> 600216 bytes public/images/avatars/pedro-duarte.png | Bin 0 -> 805238 bytes public/images/avatars/pierre-bertet.png | Bin 0 -> 146680 bytes public/images/avatars/vlad-moroz.png | Bin 0 -> 562534 bytes public/images/avatars/yohan.jpg | Bin 48335 -> 0 bytes public/images/avatars/ze-meirinhos.png | Bin 0 -> 666839 bytes public/images/avatars/ze.jpg | Bin 66991 -> 0 bytes public/images/blocks-thumb-large.jpg | Bin 0 -> 689748 bytes public/images/blocks-thumb.jpg | Bin 515658 -> 0 bytes public/images/brandwatch-hero.jpg | Bin 1370481 -> 0 bytes public/images/brandwatch-social-preview.jpg | Bin 329580 -> 0 bytes public/images/brandwatch-thumb-small.jpg | Bin 1016448 -> 0 bytes public/images/brandwatch-thumb.jpg | Bin 1875728 -> 0 bytes public/images/bright-thumb.jpg | Bin 1219252 -> 0 bytes ...sign-system.jpg => dash-design-system.jpg} | Bin .../images/{bright-hero.jpg => dash-hero.jpg} | Bin .../{bright-dash-intro.jpg => dash-intro.jpg} | Bin ...al-preview.jpg => dash-social-preview.jpg} | Bin public/images/dash-thumb-large.jpg | Bin 0 -> 2200101 bytes ...t-thumb-small.jpg => dash-thumb-small.jpg} | Bin ...ht-dash-ui-admin.jpg => dash-ui-admin.jpg} | Bin ...-dash-ui-detail.jpg => dash-ui-detail.jpg} | Bin ...ight-dash-ui-edit.jpg => dash-ui-edit.jpg} | Bin ...ht-dash-ui-login.jpg => dash-ui-login.jpg} | Bin ...-dash-ui-search.jpg => dash-ui-search.jpg} | Bin public/images/logos/andyhook-mark.svg | 1 - public/images/logos/aragon-mark.svg | 1 - public/images/logos/brandwatch-mark.svg | 1 - public/images/logos/bright-mark.svg | 1 - public/images/logos/modulz-mark.svg | 1 - public/images/logos/tjc-mark.svg | 1 - public/images/radix-thumb-large.jpg | Bin 0 -> 2183702 bytes public/images/radix-thumb-small.jpg | Bin 0 -> 1159241 bytes public/images/social-preview.png | Bin 229527 -> 0 bytes public/images/test.png | Bin 555 -> 0 bytes public/manrope-variable.ttf | Bin 155416 -> 0 bytes public/next.svg | 1 + public/vercel.svg | 1 + serviceWorker.ts | 11 - style/font.css | 6 - style/global.tsx | 170 - style/motion.ts | 22 - style/responsive.spec.ts | 15 - style/responsive.ts | 54 - style/theme.ts | 192 - style/typography.ts | 271 - style/utils.spec.tsx | 73 - style/utils.ts | 125 - styled-components.d.ts | 17 - tailwind.config.ts | 71 + theme.ts | 130 + tsconfig.json | 35 +- types.ts | 91 + utils/general.spec.ts | 29 - utils/general.ts | 48 - utils/testing.tsx | 51 - vercel.json | 5 - yarn.lock | 6428 ----------------- 233 files changed, 11732 insertions(+), 14118 deletions(-) delete mode 100644 .babelrc delete mode 100644 .prettierrc create mode 100644 app/(index)/experience-list.tsx create mode 100644 app/(index)/experience.tsx create mode 100644 app/(index)/page.tsx create mode 100644 app/(index)/testimonials.tsx create mode 100644 app/(index)/work.tsx create mode 100644 app/[projectId]/_components/content-section.tsx create mode 100644 app/[projectId]/_components/content/aragon.tsx create mode 100644 app/[projectId]/_components/content/blocks.tsx create mode 100644 app/[projectId]/_components/content/dash.tsx create mode 100644 app/[projectId]/_components/content/radix.tsx create mode 100644 app/[projectId]/_components/image-group-section.tsx create mode 100644 app/[projectId]/_components/image-section.tsx create mode 100644 app/[projectId]/_components/team-list.tsx create mode 100644 app/[projectId]/_components/tooltip-link.tsx create mode 100644 app/[projectId]/page.tsx create mode 100644 app/favicon.ico create mode 100644 app/globals.css create mode 100644 app/layout.tsx create mode 100644 app/opengraph-image.jpg create mode 100644 app/router.tsx create mode 100644 app/sidebar.tsx create mode 100644 app/theme.tsx delete mode 100644 components/About/About.tsx delete mode 100644 components/AccessibleIcon/AccessibleIcon.tsx delete mode 100644 components/Button/Button.tsx delete mode 100644 components/ExperienceList/ExperienceList.tsx delete mode 100644 components/Footer/Footer.tsx delete mode 100644 components/GradientText/GradientText.tsx delete mode 100644 components/Icon/Icon.tsx delete mode 100644 components/ImageBase/ImageBase.tsx delete mode 100644 components/InteractionBase/InteractionBase.spec.tsx delete mode 100644 components/InteractionBase/InteractionBase.tsx delete mode 100644 components/Layout/LayoutGutter.tsx delete mode 100644 components/Layout/LayoutLimiter.tsx delete mode 100644 components/Layout/LayoutRow.tsx delete mode 100644 components/Layout/LayoutShade.tsx delete mode 100644 components/Link/Link.tsx delete mode 100644 components/LoadingIndicator/LoadingIndicator.tsx delete mode 100644 components/Logo/Logo.tsx delete mode 100644 components/Menu/Menu.tsx delete mode 100644 components/Menu/MenuPanel.tsx delete mode 100644 components/Menu/MenuTrigger.tsx delete mode 100644 components/Meta/MetaBrowser.tsx delete mode 100644 components/Meta/MetaIcons.tsx delete mode 100644 components/Meta/MetaSocial.tsx delete mode 100644 components/MoreProjects/MoreProjects.tsx delete mode 100644 components/Panel/Panel.tsx delete mode 100644 components/Pip/Pip.tsx delete mode 100644 components/Project/ProjectDescription.tsx delete mode 100644 components/Project/ProjectImage.tsx delete mode 100644 components/Project/ProjectImageGroup.tsx delete mode 100644 components/Project/ProjectQuote.tsx delete mode 100644 components/ProjectCard/ProjectCard.tsx delete mode 100644 components/ProjectTemplate/ProjectTemplate.tsx delete mode 100644 components/QuoteCard/QuoteCard.tsx delete mode 100644 components/RemoveWidow/RemoveWidow.tsx delete mode 100644 components/ScrollReveal/ScrollReveal.tsx delete mode 100644 components/Signature/Signature.tsx delete mode 100644 components/SocialIcons/SocialIcons.tsx delete mode 100644 components/SocialProof/SocialProof.tsx delete mode 100644 components/StripeBackground/StripeBackground.tsx delete mode 100644 components/Text/TextBase.tsx delete mode 100644 components/Text/TextHeading.tsx delete mode 100644 components/Text/TextParagraph.tsx delete mode 100644 components/TopBar/TopBar.tsx create mode 100644 components/author.tsx create mode 100644 components/container.tsx create mode 100644 components/copy.tsx create mode 100644 components/download-button.tsx create mode 100644 components/external-favicon.tsx create mode 100644 components/external-link.tsx create mode 100644 components/focus-ring.tsx create mode 100644 components/gutter.tsx create mode 100644 components/hatch.tsx create mode 100644 components/icons/dribbble-icon.tsx create mode 100644 components/icons/github-icon.tsx create mode 100644 components/icons/instagram-icon.tsx create mode 100644 components/icons/linkedin-icon.tsx create mode 100644 components/icons/twitter-icon.tsx create mode 100644 components/line.tsx create mode 100644 components/logos/aragon-mark.tsx create mode 100644 components/logos/brandwatch-mark.tsx create mode 100644 components/logos/bright-mark.tsx create mode 100644 components/logos/freelance-mark.tsx create mode 100644 components/logos/modulz-mark.tsx create mode 100644 components/logos/scroll-mark.tsx create mode 100644 components/logos/tjc-mark.tsx create mode 100644 components/logos/workos-mark.tsx create mode 100644 components/primitives/accordion.tsx create mode 100644 components/primitives/collapsible.tsx create mode 100644 components/primitives/collection.tsx create mode 100644 components/primitives/hover-group.tsx create mode 100644 components/primitives/mouse-hover.tsx create mode 100644 components/primitives/primitive.tsx create mode 100644 components/primitives/scroll-area.tsx create mode 100644 components/primitives/slot.tsx create mode 100644 components/primitives/tooltip.tsx create mode 100644 components/quote.tsx create mode 100644 components/scramble-text.tsx create mode 100644 components/social-link.tsx create mode 100644 components/spinner.tsx create mode 100644 components/tooltip.tsx create mode 100644 components/utils/compose-refs.tsx create mode 100644 components/utils/create-context.tsx create mode 100644 components/utils/use-callback-ref.tsx create mode 100644 components/utils/use-controllable-state.tsx create mode 100644 components/utils/use-device.tsx create mode 100644 components/utils/use-layout-effect.tsx create mode 100644 components/utils/use-screen.tsx create mode 100644 components/work-item.tsx create mode 100644 cva.config.ts create mode 100644 data.ts delete mode 100644 data/icons.ts delete mode 100644 data/images.ts delete mode 100644 data/meta.ts delete mode 100644 data/projects.ts delete mode 100644 data/recommendations.ts delete mode 100644 generate-sizes.js delete mode 100644 hooks/useDetectOutsideClick/useDetectOutsideClick.tsx delete mode 100644 hooks/useFocusVisible/useFocusVisible.spec.tsx delete mode 100644 hooks/useFocusVisible/useFocusVisible.tsx delete mode 100644 hooks/useLoadPercentage/useLoadPercentage.spec.tsx delete mode 100644 hooks/useLoadPercentage/useLoadPercentage.tsx delete mode 100644 hooks/useLocationState/useLocationState.tsx delete mode 100644 hooks/useRelativeYMotion/getRelativeMotionProps.ts delete mode 100644 hooks/useRelativeYMotion/useRelativeYMotion.spec.tsx delete mode 100644 hooks/useRelativeYMotion/useRelativeYMotion.tsx delete mode 100644 hooks/useScrollPosition/useScrollPosition.tsx delete mode 100644 hooks/useTheme/useTheme.tsx create mode 100644 images.ts delete mode 100644 jest.setup.ts delete mode 100644 next.config.js create mode 100644 next.config.mjs delete mode 100644 pages/_app.tsx delete mode 100644 pages/_document.tsx delete mode 100644 pages/aragon.tsx delete mode 100644 pages/blocks.tsx delete mode 100644 pages/bright.tsx delete mode 100644 pages/index.tsx create mode 100644 pnpm-lock.yaml create mode 100644 postcss.config.mjs rename public/{andy-hook-brighton-senior-ui-engineer.pdf => andy-hook-uk-software-engineer.pdf} (100%) delete mode 100644 public/favicon.ico delete mode 100644 public/icons/icon-144x144.png delete mode 100644 public/icons/icon-192x192.png delete mode 100644 public/icons/icon-256x256.png delete mode 100644 public/icons/icon-32x32.png delete mode 100644 public/icons/icon-384x384.png delete mode 100644 public/icons/icon-48x48.png delete mode 100644 public/icons/icon-512x512.png delete mode 100644 public/icons/icon-72x72.png delete mode 100644 public/icons/icon-96x96.png create mode 100644 public/images/aragon-thumb-large.jpg delete mode 100644 public/images/aragon-thumb.jpg create mode 100644 public/images/avatars/adri-garcia.png create mode 100644 public/images/avatars/alastair-bird.png create mode 100644 public/images/avatars/andrew-khan.png delete mode 100644 public/images/avatars/andrew.jpg create mode 100644 public/images/avatars/ben-browning.png delete mode 100644 public/images/avatars/ben.jpg create mode 100644 public/images/avatars/benoit-grelard.png create mode 100644 public/images/avatars/brett-sun.png delete mode 100644 public/images/avatars/brett.jpg create mode 100644 public/images/avatars/jenna-smith.png delete mode 100644 public/images/avatars/jo.jpg create mode 100644 public/images/avatars/michael-allan.png delete mode 100644 public/images/avatars/michael.jpg create mode 100644 public/images/avatars/paty-davila.png create mode 100644 public/images/avatars/pedro-duarte.png create mode 100644 public/images/avatars/pierre-bertet.png create mode 100644 public/images/avatars/vlad-moroz.png delete mode 100644 public/images/avatars/yohan.jpg create mode 100644 public/images/avatars/ze-meirinhos.png delete mode 100644 public/images/avatars/ze.jpg create mode 100644 public/images/blocks-thumb-large.jpg delete mode 100644 public/images/blocks-thumb.jpg delete mode 100644 public/images/brandwatch-hero.jpg delete mode 100644 public/images/brandwatch-social-preview.jpg delete mode 100644 public/images/brandwatch-thumb-small.jpg delete mode 100644 public/images/brandwatch-thumb.jpg delete mode 100644 public/images/bright-thumb.jpg rename public/images/{bright-dash-design-system.jpg => dash-design-system.jpg} (100%) rename public/images/{bright-hero.jpg => dash-hero.jpg} (100%) rename public/images/{bright-dash-intro.jpg => dash-intro.jpg} (100%) rename public/images/{bright-social-preview.jpg => dash-social-preview.jpg} (100%) create mode 100644 public/images/dash-thumb-large.jpg rename public/images/{bright-thumb-small.jpg => dash-thumb-small.jpg} (100%) rename public/images/{bright-dash-ui-admin.jpg => dash-ui-admin.jpg} (100%) rename public/images/{bright-dash-ui-detail.jpg => dash-ui-detail.jpg} (100%) rename public/images/{bright-dash-ui-edit.jpg => dash-ui-edit.jpg} (100%) rename public/images/{bright-dash-ui-login.jpg => dash-ui-login.jpg} (100%) rename public/images/{bright-dash-ui-search.jpg => dash-ui-search.jpg} (100%) delete mode 100644 public/images/logos/andyhook-mark.svg delete mode 100644 public/images/logos/aragon-mark.svg delete mode 100644 public/images/logos/brandwatch-mark.svg delete mode 100644 public/images/logos/bright-mark.svg delete mode 100644 public/images/logos/modulz-mark.svg delete mode 100644 public/images/logos/tjc-mark.svg create mode 100644 public/images/radix-thumb-large.jpg create mode 100644 public/images/radix-thumb-small.jpg delete mode 100644 public/images/social-preview.png delete mode 100644 public/images/test.png delete mode 100644 public/manrope-variable.ttf create mode 100644 public/next.svg create mode 100644 public/vercel.svg delete mode 100644 serviceWorker.ts delete mode 100644 style/font.css delete mode 100644 style/global.tsx delete mode 100644 style/motion.ts delete mode 100644 style/responsive.spec.ts delete mode 100644 style/responsive.ts delete mode 100644 style/theme.ts delete mode 100644 style/typography.ts delete mode 100644 style/utils.spec.tsx delete mode 100644 style/utils.ts delete mode 100644 styled-components.d.ts create mode 100644 tailwind.config.ts create mode 100644 theme.ts create mode 100644 types.ts delete mode 100644 utils/general.spec.ts delete mode 100644 utils/general.ts delete mode 100644 utils/testing.tsx delete mode 100644 vercel.json delete mode 100644 yarn.lock diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 854cb73..0000000 --- a/.babelrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "presets": ["next/babel"], - "plugins": [["styled-components", { "ssr": true }]] -} diff --git a/.eslintrc.json b/.eslintrc.json index 037f59a..4f63ca7 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,46 +1,14 @@ { - "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], "extends": [ + "next/core-web-vitals", "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "prettier/@typescript-eslint", - "plugin:prettier/recommended", - "plugin:react/recommended", - "plugin:react-hooks/recommended" + "plugin:@typescript-eslint/recommended" ], - "plugins": ["@typescript-eslint", "react", "prettier"], - "parserOptions": { - "jsx": true, - "project": "./tsconfig.json" - }, - "settings": { - "react": { - "version": "detect" - } - }, "rules": { - "react/react-in-jsx-scope": "off", - "react/no-unescaped-entities": "off", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/no-empty-interface": [ - "error", - { - "allowSingleExtends": true - } - ] - }, - "overrides": [ - { - "files": ["*.js"], - "rules": { - "@typescript-eslint/no-var-requires": "off" - } - } - ], - "env": { - "browser": true, - "es6": true, - "node": true, - "jest": true + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": "error", + "@typescript-eslint/no-empty-object-type": "off", + "@typescript-eslint/no-explicit-any": "off" } } diff --git a/.gitignore b/.gitignore index aa3b2f1..e3b3fe7 100644 --- a/.gitignore +++ b/.gitignore @@ -31,7 +31,4 @@ yarn-error.log* .env.production.local # vercel -.vercel - -# generated files -/data/image-sizes.json \ No newline at end of file +.vercel \ No newline at end of file diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index b2095be..0000000 --- a/.prettierrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "semi": false, - "singleQuote": true -} diff --git a/LICENSE b/LICENSE index 8a938b8..66c4105 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 Andy Hook +Copyright (c) 2025 Andy Hook Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 1adb9f6..a9e6a71 100644 --- a/README.md +++ b/README.md @@ -1,56 +1,16 @@ # andyhook.dev -Source code for my personal portfolio. Built with [Next.js](https://nextjs.org/), [Styled Components](https://styled-components.com/) and [Framer Motion](https://www.framer.com/api/motion/). +Source code for my personal portfolio. Built with [Next.js](https://nextjs.org/), [Tailwind](https://tailwindcss.com/) and [Motion](https://motion.dev/). ## Development To start development: ```sh -yarn dev +pnpm dev ``` -By default this will spin up a local server on `localhost:3000` - -## Image dimensions - -Static image dimensions are generated during a build step, either on each run of the dev server or for every production build. If you add, update or change any images you must run `yarn generate-image-sizes` or restart the dev server to get the latest dimensions and [ImagePath](https://github.com/andy-hook/andyhook.dev/blob/main/data/images.ts) typings. - -## Building - -To build for Node environments: - -```sh -yarn build -``` - -or for a [static export](https://nextjs.org/docs/advanced-features/static-html-export): - -```sh -yarn build-static -``` - -## Testing - -Run the tests: - -```sh -yarn test -``` - -## Type checking and linting - -Type checking and linting is handled by a single script: - -```sh -yarn lint -``` - -## Formatting your code - -Prettier is supported but you'll need to add your own script if you wish to use the cli. - -I personally use [this vscode package](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) to format automatically within my editor. +By default this will spin up a local server on `localhost:3001` ## License diff --git a/app/(index)/experience-list.tsx b/app/(index)/experience-list.tsx new file mode 100644 index 0000000..1cc802c --- /dev/null +++ b/app/(index)/experience-list.tsx @@ -0,0 +1,364 @@ +'use client'; + +import * as React from 'react'; +import { PlusIcon } from '@heroicons/react/16/solid'; +import { AnimatePresence, cubicBezier, motion } from 'motion/react'; +import { cx } from '@/cva.config'; + +import * as AccordionPrimitive from '@/components/primitives/accordion'; + +import { useCallbackRef } from '@/components/utils/use-callback-ref'; +import { useScreen } from '@/components/utils/use-screen'; +import { Line } from '@/components/line'; +import { Hatch } from '@/components/hatch'; + +import { experience } from '@/data'; +import { useLayoutEffect } from '@/components/utils/use-layout-effect'; +import { ScrambleText } from '@/components/scramble-text'; +import { getColorSlateDark } from '@/theme'; +import * as HoverGroup from '@/components/primitives/hover-group'; +import { ExternalLink } from '@/components/external-link'; +import { FocusRing } from '@/components/focus-ring'; + +const MOTION_TRANSITION = { + ease: cubicBezier(0.5, 0.2, 0.2, 1), + duration: 0.3, + opacity: { ease: 'linear', duration: 0.15 }, +}; + +type ExperienceListElement = React.ComponentRef<'div'>; + +interface ExperienceListProps extends React.ComponentPropsWithoutRef<'div'> {} + +export const ExperienceList = React.forwardRef( + ({ className, ...props }, forwardedRef) => { + const [openItem, onOpenItemChange] = React.useState(''); + const [hoveredItem, setHoveredItem] = React.useState(''); + const animateCompanyName = useScreen({ max: 'md' }); + + return ( +
+
+ + + +
+ +
+ + +
    + {experience.map((entry, i) => { + const firstItem = i === 0; + const lastItem = experience.length - 1 === i; + const key = entry.company; + const open = openItem === key; + const hovered = hoveredItem === key; + const Logo = entry.logoAsset; + + return ( + +
  • + + {(open || hovered) && ( + + )} + + + {!lastItem ? ( + + ) : null} + + + + + {({ yearScrambleRef, companyScrambleRef, titleScrambleRef }) => ( + <> +
    +
    + + {entry.year} + + +
    {entry.year}
    +
    +
    + +
    + +
    + + + + + {entry.company} + + + + + {!open && ( + + + {entry.title} + + + )} + + + +
    +
    + + + +
    +
    + + )} +
    +
    + + onOpenItemChange(key)}> +
    + + + + +

    + {entry.description} +

    + +
    + {[ + ['Home', entry.link], + ['Role', entry.title], + ['Tenure', entry.tenure], + ].map(([label, value]) => ( +
    +
    + {label} +
    + + {value === entry.link ? ( + + {value} + + ) : ( +
    + {value} +
    + )} +
    + ))} +
    +
    +
    +
    +
  • +
    + ); + })} +
+
+
+
+
+ ); + }, +); + +ExperienceList.displayName = 'ExperienceList'; + +function getCompanyTitleColor(openItem: string, hoveredItem: string, key: string) { + const open = openItem === key; + const hovered = hoveredItem === key; + const listHasOpen = openItem !== ''; + const listHasHovered = hoveredItem !== ''; + + if (listHasOpen) { + return getColorSlateDark(open || hovered ? 12 : 10); + } + + return getColorSlateDark(hovered || !listHasHovered ? 12 : 10); +} + +/* -----------------------------------------------------------------------------------------------*/ + +type ExperienceListItemTriggerElement = React.ComponentRef; + +interface ExperienceListItemTriggerProps + extends Omit, 'children'> { + children(refs: { + yearScrambleRef: React.RefObject | null>; + companyScrambleRef: React.RefObject | null>; + titleScrambleRef: React.RefObject | null>; + }): React.ReactNode; + open: boolean; + value: string; +} + +const ExperienceListItemTrigger = React.forwardRef< + ExperienceListItemTriggerElement, + ExperienceListItemTriggerProps +>(({ children, open, value, ...props }, forwardedRef) => { + const yearScrambleRef = React.useRef>(null); + const companyScrambleRef = React.useRef>(null); + const titleScrambleRef = React.useRef>(null); + + return ( + { + if (mode === 'hovered' && !open) { + yearScrambleRef.current?.replay(); + companyScrambleRef.current?.replay(); + titleScrambleRef.current?.replay(); + } + }} + > + + {children({ yearScrambleRef, companyScrambleRef, titleScrambleRef })} + + + ); +}); + +ExperienceListItemTrigger.displayName = 'ExperienceListItemTrigger'; + +/* -----------------------------------------------------------------------------------------------*/ + +type ExperienceListItemContentElement = React.ComponentRef; + +interface ExperienceListItemContentProps + extends React.ComponentPropsWithoutRef { + onBeforeMatch(): void; +} + +const ExperienceListItemContent = React.forwardRef< + ExperienceListItemContentElement, + ExperienceListItemContentProps +>(({ children, onBeforeMatch, ...props }, forwardedRef) => { + const handleBeforeMatch = useCallbackRef(onBeforeMatch); + const innerRef = React.useRef(null); + + useLayoutEffect(() => { + const innerContent = innerRef.current; + + if (innerContent) { + innerContent.setAttribute('hidden', 'until-found'); + innerContent.addEventListener('beforematch', handleBeforeMatch); + return () => { + innerContent.removeEventListener('beforematch', handleBeforeMatch); + }; + } + }, [handleBeforeMatch]); + + return ( + + { + if (variant === 'open') { + innerRef.current?.removeAttribute('hidden'); + } + }} + onAnimationComplete={(variant) => { + if (variant === 'closed') { + innerRef.current?.setAttribute('hidden', 'until-found'); + } + }} + > + + {children} + + + + ); +}); + +ExperienceListItemContent.displayName = 'ExperienceListItemContent'; diff --git a/app/(index)/experience.tsx b/app/(index)/experience.tsx new file mode 100644 index 0000000..b17c6f8 --- /dev/null +++ b/app/(index)/experience.tsx @@ -0,0 +1,99 @@ +import * as React from 'react'; + +import { Line } from '@/components/line'; +import { Container } from '@/components/container'; +import { Hatch } from '@/components/hatch'; + +import { ExperienceList } from './experience-list'; +import { DownloadButton } from '@/components/download-button'; +import { Gutter } from '@/components/gutter'; + +type ExperienceElement = React.ComponentRef<'section'>; + +interface ExperienceProps extends React.ComponentPropsWithoutRef<'section'> {} + +export const Experience = React.forwardRef( + (props, forwardedRef) => { + return ( +
+ + +
+

+
More than a decade building for the web
+

+ +
+

+ Design is at the heart of everything I do, I believe that a close relationship + between visual design, UX and front-end engineering expertise leads to a better + customer experience within digital products. +

+

+ As a specialist in modular design systems and component libraries, I work to + bridge the gap between design and engineering disciplines and am a catalyst for + fast, iterative processes within agile product teams. My technical experience + spans a wealth of front-end technologies ranging from modern SPA frameworks to + run-time performance profiling, testing and accessibility. +

+
+ +
+ + + + + + + +
+ + + + + +
+
+ + + + +
+
+ ); + }, +); + +Experience.displayName = 'Experience'; diff --git a/app/(index)/page.tsx b/app/(index)/page.tsx new file mode 100644 index 0000000..4acc6be --- /dev/null +++ b/app/(index)/page.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; + +import { Work } from './work'; +import { Experience } from './experience'; +import { Testimonials } from './testimonials'; +import { RouterTransition } from '../router'; + +export default function Home() { + return ( +
+ + + + + + + + + +
+ ); +} diff --git a/app/(index)/testimonials.tsx b/app/(index)/testimonials.tsx new file mode 100644 index 0000000..bb769fe --- /dev/null +++ b/app/(index)/testimonials.tsx @@ -0,0 +1,275 @@ +import * as React from 'react'; +import { cx } from '@/cva.config'; +import { getTestimonialById } from '@/data'; + +import { Container } from '@/components/container'; +import { Quote } from '@/components/quote'; +import { Line } from '@/components/line'; +import { Author } from '@/components/author'; +import { Gutter } from '@/components/gutter'; + +const vlad = getTestimonialById('vlad'); +const benoit = getTestimonialById('benoit'); +const michael = getTestimonialById('michael'); +const andrew = getTestimonialById('andrew'); +const brett = getTestimonialById('brett'); + +type TestimonialsElement = React.ComponentRef<'section'>; + +interface TestimonialsProps extends React.ComponentPropsWithoutRef<'section'> {} + +export const Testimonials = React.forwardRef( + (props, forwardedRef) => { + return ( +
+ + +
+

+
Trusted by world-class teams
+

+ +

+ I’ve worked with some amazing people throughout the years, here is what they have to + say +

+
+
+ + +
+ + + + + + + + + +
+
+
+
+ ); + }, +); + +Testimonials.displayName = 'Testimonials'; + +/* -----------------------------------------------------------------------------------------------*/ + +type TestimonialGridElement = React.ComponentRef<'div'>; + +interface TestimonialGridProps extends React.ComponentPropsWithoutRef<'div'> {} + +const TestimonialGrid = React.forwardRef( + ({ className, ...props }, forwardedRef) => { + return ( +
+ + +
+
    +
  • +
    + +
    +
    + + + +
    + + {vlad && ( +
    + + {vlad.excerpt} + + + + +
    + +
    +
    + )} +
    +
  • + +
  • + {benoit && ( + + + + )} + + + +
  • + + {michael && ( +
  • + + + + + +
  • + )} + + {andrew && ( +
  • + + + + + +
  • + )} + + {brett && ( +
  • + + + +
  • + )} +
+
+
+ ); + }, +); + +TestimonialGrid.displayName = 'TestimonialGrid'; + +/* -----------------------------------------------------------------------------------------------*/ + +type TestimonialGridItemElement = React.ComponentRef<'figure'>; + +interface TestimonialGridItemProps extends React.ComponentPropsWithoutRef<'figure'> { + content: string; +} + +const TestimonialGridItem = React.forwardRef( + ({ className, content, children, ...props }, forwardedRef) => { + return ( +
+ + {content} + + +
{children}
+
+ ); + }, +); + +TestimonialGridItem.displayName = 'TestimonialGridItem'; + +/* -----------------------------------------------------------------------------------------------*/ + +type QuoteMarkElement = React.ComponentRef<'svg'>; + +interface QuoteMarkProps extends React.ComponentPropsWithoutRef<'svg'> {} + +const QuoteMark = React.forwardRef((props, forwardedRef) => { + const uid = React.useId(); + + const gradientId = `decorative_quote_gradient_${uid}`; + + return ( + + + + + + + + + + ); +}); + +QuoteMark.displayName = 'QuoteMark'; diff --git a/app/(index)/work.tsx b/app/(index)/work.tsx new file mode 100644 index 0000000..10177ac --- /dev/null +++ b/app/(index)/work.tsx @@ -0,0 +1,244 @@ +import * as React from 'react'; + +import { Container } from '@/components/container'; +import { Line } from '@/components/line'; +import { Hatch } from '@/components/hatch'; + +import { SocialLink } from '@/components/social-link'; +import * as HoverGroup from '@/components/primitives/hover-group'; +import { WorkItem } from '@/components/work-item'; +import { getProjectById } from '@/data'; +import { RouterTransition } from '../router'; +import { cx } from '@/cva.config'; +import { Gutter } from '@/components/gutter'; + +const radix = getProjectById('radix'); +const aragon = getProjectById('aragon'); +const blocks = getProjectById('blocks'); +const dash = getProjectById('dash'); + +type WorkElement = React.ComponentRef<'section'>; + +interface WorkProps extends React.ComponentPropsWithoutRef<'section'> {} + +export const Work = React.forwardRef((props, forwardedRef) => { + return ( +
+ + +
+ + + +
+ +
+ Software Engineer +
+
+ +

+ Building next-generation user interfaces out of the UK +

+
+ + +
    + {(['github', 'linkedin', 'dribbble', 'twitter', 'instagram'] as const).map( + (platform) => ( +
  • + +
  • + ), + )} +
+
+
+
+ + +
+
+ + + + + + +
+ +
+ + +
    +
  • + + + {radix && } +
  • + +
  • + + + {aragon && } +
  • + +
  • {blocks && }
  • + +
  • + + + + {dash && } +
  • +
+
+
+ + + +
+ ); +}); + +Work.displayName = 'Work'; + +/* -----------------------------------------------------------------------------------------------*/ + +type HeroMarkElement = React.ComponentRef<'svg'>; + +interface HeroMarkProps extends React.ComponentPropsWithoutRef<'svg'> {} + +const HeroMark = React.forwardRef((props, forwardedRef) => { + const uid = React.useId(); + + const radialGradientId = `hero_mark_radial_gradient_${uid}`; + const verticalGradientId1 = `hero_mark_vertical_gradient_1_${uid}`; + const verticalGradientId2 = `hero_mark_vertical_gradient_2_${uid}`; + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}); + +HeroMark.displayName = 'HeroMark'; diff --git a/app/[projectId]/_components/content-section.tsx b/app/[projectId]/_components/content-section.tsx new file mode 100644 index 0000000..7011974 --- /dev/null +++ b/app/[projectId]/_components/content-section.tsx @@ -0,0 +1,51 @@ +import * as React from 'react'; +import { Line } from '@/components/line'; +import { Hatch } from '@/components/hatch'; +import { Gutter } from '@/components/gutter'; +import { Container } from '@/components/container'; +import { RouterTransition } from '@/app/router'; + +type ContentSectionElement = React.ComponentRef<'section'>; + +interface ContentSectionProps extends React.ComponentPropsWithoutRef<'section'> { + title: string; +} + +export const ContentSection = React.forwardRef( + ({ children, title, ...props }, forwardedRef) => { + return ( +
+ + + +

+ + + + + +
{title}
+

+ +
+ {children} +
+
+
+
+
+ ); + }, +); + +ContentSection.displayName = 'ContentSection'; diff --git a/app/[projectId]/_components/content/aragon.tsx b/app/[projectId]/_components/content/aragon.tsx new file mode 100644 index 0000000..8ad7d0a --- /dev/null +++ b/app/[projectId]/_components/content/aragon.tsx @@ -0,0 +1,108 @@ +import { ContentSection } from '../content-section'; +import { ImageSection } from '../image-section'; +import { ImageGroupSection } from '../image-group-section'; +import { TooltipLink } from '../tooltip-link'; +import { + aragonIntroImage, + aragonComponentsImage, + aragonNetworkDashboardHomeImage, + aragonNetworkDashboardProposalImage, + aragonNetworkDashboardAgreementImage, + aragonUpgradeHomeImage, + aragonUpgradeConverterImage, + aragonUpgradeCompleteImage, +} from '@/images'; + +export default function AragonContent() { + return ( + <> + +

+ I joined the Aragon team in 2020 as a Senior Engineer to help further their mission of + revolutionising governance. From the very start it was clear that high quality, responsive + and delightful user interfaces were a crucial element of the project and that the team + values technically competent engineers equally versed in design. +

+

+ The utility of a{' '} + + Decentralized Autonomous Organisation + {' '} + (DAO) is well understood within the Ethereum community, but for the uninitiated there is a + lot to unpack. The team knew that onboarding an entire class of first-time crypto users to + the decentralized governance concept couldn't be achieved overnight and Aragons + messaging has taken multiple iterations over the years with this in mind. +

+

+ As part of the front-end engineering team I heavily contributed to the initial prototype + of the Aragon Network Dashboard, launched a highly praised ANT Upgrade Portal, furthered + the adoption of TypeScript in front-end code, improved and maintained a variety of open + source packages, pushed for a bigger emphasis on Agile development and mentored junior + team members. +

+
+ + + + +

+ The project is well known in the DAO space for executing to a very high standard, the + exceptional quality of their brand design and user experience stand out in an industry + that is not known for being particularly user friendly. +

+

+ I was lucky enough to work with two amazing design talents, Patricia Davila and Adrián + García, user experience, and brand respectively. They are some of the most talented + designers I've worked with and deserve high praise for the visual fidelity and + intuitive experience offered within Aragons products. +

+

+ An important component of a high performing product team is a strong relationship between + engineers and designers. Catching every edge case, accounting for all possible UI states + and fine tuning a flow to fit within technological limitations requires smooth + communication between disciplines and engineers who understand all sides of the coin – + technology, design, users and business. My biggest impact in this regard was an ability to + execute on this vision, ensuring every detail and interaction was of the highest quality. +

+
+ + + + +

+ Quality is often considered diametrically opposed to delivery speed, and in a lot of + circumstances this can be the case, however, my take on this is to ask the question of + why? why are we building this now? what's the simplest feature we can ship today that + adds value for users? These are important questions to ask, a mutual understanding of + expectations within the team and a tight scope can unlock a team to push the quality of + what is delivered while fostering an iterative development culture that empowers a team to + rapidly evolve at a predictable cadance. +

+

+ From a technology perspective I'm a believer in the use of static type systems such + as TypeScript for + improving velocity over time, the confidence that type systems provide when refactoring, + and the implicit documentation provided by strict typings go a long way to battling code + entropy (and make it a whole lot easier to onboard new hires) +

+

+ By following these principles we were able to deliver high impact initiatives beyond + expectation and ahead of schedule. The launch of the ANT Upgrade Portal was a great + example of this and proved the benefits to the team. +

+
+ + + + ); +} diff --git a/app/[projectId]/_components/content/blocks.tsx b/app/[projectId]/_components/content/blocks.tsx new file mode 100644 index 0000000..2dd8fb7 --- /dev/null +++ b/app/[projectId]/_components/content/blocks.tsx @@ -0,0 +1,73 @@ +import { ImageGroupSection } from '../image-group-section'; +import { ContentSection } from '../content-section'; +import { ImageSection } from '../image-section'; +import { TooltipLink } from '../tooltip-link'; +import { + blocksDetailImage, + blocksHomeImage, + blocksIntroImage, + blocksListImage, + blocksTransactionsImage, +} from '@/images'; + +export default function BlocksContent() { + return ( + <> + +

+ I designed and built the first version of Blocks in early 2020 as part of the technical + assessment process at Aragon, I wanted to design a tightly scoped app that was visually + exciting and offered opportunities for interesting interactions. While the utility of the + app is not comparable to the best of existing tools, it still gave me the opportunity to + learn more about Ethereum and + experiment with some new front-end techniques. +

+

+ An interesting byproduct of this project was being able to receive critique and feedback + on my implementation during the assessment, this effectively turned the process into an + additional knowledge stream and provided learnings which I could cycle back into the code, + further improving it for my own needs. While I invested time above and beyond what is + typical, I found it exciting to work on and helpful for professional growth. +

+
+ + + + +

+ Blocks was originally using{' '} + Web3.js and a{' '} + MetaMask provider to fetch block + information, this was the simplest way to get started but had big drawbacks – primarily + the need to have a wallet installed and connected before being able to fetch data. + Migrating to Ethers.js offered + a more intuitive, opinionated API while using Infura, Alchemy and Etherscan backed nodes + solved the wallet provider problem. That said, many of these concepts were new to me, and + while the typical centralised stack is mature and fast, Web3 initially felt cumbersome and + difficult to use, which is only made more obvious from the proliferation of excellently + documented, low cost data services available with the former. +

+

+ Another aspect I had to consider was that interacting with a chain directly from a client + requires that the front-end handles much of the data massaging and manipulation when a + centralised backend service would normally handle this work. I found myself having to be + careful not to carelessly perform calculations with standard Number types and make ample + use of Big Number formatting libraries to guard against safe range errors. +

+

+ Tech moves fast though, and we now have fantastic projects such as{' '} + The Graph offering a data + abstraction layer powered by Subgraphs and GraphQL, we even have native support for BigInt + directly in the browser, greatly reducing bundle size and standardising around a single + API. It's these types of learnings and exposure to new technology that make investing + in personal projects rewarding and valuable. +

+
+ + + + ); +} diff --git a/app/[projectId]/_components/content/dash.tsx b/app/[projectId]/_components/content/dash.tsx new file mode 100644 index 0000000..d404ca9 --- /dev/null +++ b/app/[projectId]/_components/content/dash.tsx @@ -0,0 +1,102 @@ +import { ImageGroupSection } from '../image-group-section'; +import { ContentSection } from '../content-section'; +import { ImageSection } from '../image-section'; +import { + dashIntroImage, + dashDesignSystemImage, + dashUILoginImage, + dashUISearchImage, + dashUIEditImage, + dashUIDetailImage, + dashUIAdminImage, +} from '@/images'; +import { TooltipLink } from '../tooltip-link'; +export default function DashContent() { + return ( + <> + +

+ In 2018 I joined the Bright team to evolve and elevate the user interface of Dash, a new + streamlined digital asset management product that the team were busy preparing to ship as + an MVP. +

+

+ Digital assets are not to be confused with digital currencies in this context. Digital + assets can be anything from images and videos to documents and spreadsheets, anything an + organisation would need to align around and provide easy access to, whether for marketing, + operations or commerce. The unique selling point with Dash is the ability to easily find + and share exactly what you need. A large library of assets can be effortlessly categorised + and organised through the use of facial recognition, in scene object tagging and metadata + extraction. +

+

+ For Dash, the team were very clear that they really wanted to have a delightful, + high-quality user experience and felt their visual design at the time, didn't quite + deliver on that promise. I ultimately contributed to heavily redesigning the visual + aesthetic and helped ship a consistent, portable UI kit, styling system and various high + impact features on the front-end. +

+
+ + + + +

+ The first area that I looked at was how the team were approaching visual style within the + application, Dash is an Angular SPA + supported by a micro-service backend architecture and made heavy use of Google{' '} + Material design principles and + their associated UI primitives. While Material principles are solid and offer great + documentation for aligning a team, the visual style itself lacks unique character and + often illicits a heavy association with Google products. +

+

+ Having taken the time to understand the problem space, get familiar with the codebase and + liase with the brand team, I drew up a set of guiding principles and aspirational designs + that would influence the visual style and align it with a yet to be revealed company wide + rebrand. +

+

+ The codebase at that time did feature an initial design token implementation but the + application of it was spotty and inconsistent. I worked with the team to build out a + revised set of tokens by extending and improving the existing{' '} + Sass (SCSS) utilities, from + spacing and typographic scales to colour palettes and a custom theming system, this would + go on to be exposed to end-users via a white labelling feature in the dashboard. +

+
+ + + + +

+ Over time we continued to build out the component system while also shipping features and + iterating based on customer feedback. We would later encounter technical limitations such + as poor performance within our list view with large collections. I worked with the team to + successfully implement a virtual viewport which enabled us to clamp the number of rendered + nodes and improve performance. +

+

+ As a small team with a lean start-up mentality, we had to tackle each and every problem + with ruthless prioritisation and make the right choices of when to favour features over + improving our developer experience or pay down technical debt. This is the reality of + quickly shipping value to users and is even more crucial as we were in the validation + stage and needed to quickly test product decisions to find fit. I feel proud to have been + able to provide high impact UI engineering and deliver on one of the teams core pillars in + delivering a slick, delightful user experience. +

+
+ + + + ); +} diff --git a/app/[projectId]/_components/content/radix.tsx b/app/[projectId]/_components/content/radix.tsx new file mode 100644 index 0000000..0afc6c5 --- /dev/null +++ b/app/[projectId]/_components/content/radix.tsx @@ -0,0 +1,3 @@ +export default function RadixContent() { + return <>; +} diff --git a/app/[projectId]/_components/image-group-section.tsx b/app/[projectId]/_components/image-group-section.tsx new file mode 100644 index 0000000..ae76400 --- /dev/null +++ b/app/[projectId]/_components/image-group-section.tsx @@ -0,0 +1,101 @@ +import * as React from 'react'; +import { cva } from '@/cva.config'; +import { ImageWithMetadata, ProjectId } from '@/types'; + +import { Gutter } from '@/components/gutter'; +import { Container } from '@/components/container'; +import { Line } from '@/components/line'; +import { Hatch } from '@/components/hatch'; +import { RouterImage, RouterTransition } from '@/app/router'; + +const imageGroupSectionBackground = cva({ + base: 'bg-gradient-to-br px-4 md:px-12 lg:px-16 rounded-2xl md:rounded-3xl overflow-hidden', + variants: { + project: { + radix: 'from-radix-3 to-radix-1', + aragon: 'from-aragon-5 to-aragon-1', + blocks: 'from-blocks-3 to-blocks-1', + dash: 'from-dash-3 to-dash-1', + }, + }, +}); + +type ImageGroupSectionElement = React.ComponentRef<'section'>; + +interface ImageGroupSectionProps extends React.ComponentPropsWithoutRef<'section'> { + images: ImageWithMetadata[]; + project: ProjectId; +} + +export const ImageGroupSection = React.forwardRef( + ({ images, project, ...props }, forwardedRef) => { + return ( +
+ + +
+ + + +
+ {images.map((image) => { + return ( +
+ + + + +
+ +
+
+ ); + })} +
+
+
+
+
+
+ ); + }, +); + +ImageGroupSection.displayName = 'ImageGroupSection'; diff --git a/app/[projectId]/_components/image-section.tsx b/app/[projectId]/_components/image-section.tsx new file mode 100644 index 0000000..b7e9964 --- /dev/null +++ b/app/[projectId]/_components/image-section.tsx @@ -0,0 +1,49 @@ +import * as React from 'react'; +import { Gutter } from '@/components/gutter'; +import { Line } from '@/components/line'; +import { Container } from '@/components/container'; +import { RouterImage, RouterTransition } from '@/app/router'; +import { ImageWithMetadata } from '@/types'; + +type ImageSectionElement = React.ComponentRef<'section'>; + +interface ImageSectionProps extends React.ComponentPropsWithoutRef<'section'> { + image: ImageWithMetadata; +} + +export const ImageSection = React.forwardRef( + ({ image, ...props }, forwardedRef) => { + return ( +
+ + + +
+ + + + + + + +
+ +
+
+
+
+
+
+ ); + }, +); + +ImageSection.displayName = 'ImageSection'; diff --git a/app/[projectId]/_components/team-list.tsx b/app/[projectId]/_components/team-list.tsx new file mode 100644 index 0000000..95bdca4 --- /dev/null +++ b/app/[projectId]/_components/team-list.tsx @@ -0,0 +1,147 @@ +'use client'; + +import * as React from 'react'; +import { cx } from '@/cva.config'; + +import { Tooltip } from '@/components/tooltip'; +import { motion } from 'motion/react'; +import { MouseHover } from '@/components/primitives/mouse-hover'; +import { useComposedRefs } from '@/components/utils/compose-refs'; +import { RouterImage, RouterLink } from '@/app/router'; +import { ImageWithMetadata } from '@/types'; +import { FocusRing } from '@/components/focus-ring'; + +/* ------------------------------------------------------------------------------------------------- + * TeamList + * -----------------------------------------------------------------------------------------------*/ + +type TeamListElement = React.ComponentRef; + +interface TeamListProps extends React.ComponentPropsWithoutRef { + team: { avatar: ImageWithMetadata; name: string; role: string; bio: string }[]; + additionalCount: number; +} + +export const TeamList = React.forwardRef( + ({ className, team, additionalCount, ...props }, forwardedRef) => { + const ref = React.useRef(null); + const composedRefs = useComposedRefs(forwardedRef, ref); + const [expanded, setExpanded] = React.useState(false); + + return ( + { + const hasFocus = ref.current?.contains(document.activeElement); + setExpanded(hasFocus || hovered); + }} + > + setExpanded(true)} + onBlur={() => setExpanded(false)} + animate={expanded ? 'expanded' : 'initial'} + > + {team.map((member, i) => ( + +
+ {member.name} +
+
{member.role}
+ + } + > + + + +
+ ))} + +
+ +{additionalCount} +
+
+
+
+ ); + }, +); + +TeamList.displayName = 'TeamList'; + +/* -----------------------------------------------------------------------------------------------*/ + +type TeamListItemElement = React.ComponentRef; + +interface TeamListItemProps + extends Omit, 'content'> { + content: React.ReactNode; + children: React.ReactNode; + teamCount: number; + memberIndex: number; +} + +const TeamListItem = React.forwardRef( + ({ content, children, teamCount, memberIndex, ...itemProps }, forwardedRef) => { + const [active, setActive] = React.useState(false); + + const expandedOffset = -(teamCount - 1 - memberIndex) * 12 - 36; + + return ( + + + setActive(true)} + onBlur={() => setActive(false)} + onValueChange={setActive} + > + + {children} + + + + + ); + }, +); + +TeamListItem.displayName = 'TeamListItem'; diff --git a/app/[projectId]/_components/tooltip-link.tsx b/app/[projectId]/_components/tooltip-link.tsx new file mode 100644 index 0000000..08924f3 --- /dev/null +++ b/app/[projectId]/_components/tooltip-link.tsx @@ -0,0 +1,43 @@ +import * as React from 'react'; + +import { Tooltip } from '@/components/tooltip'; +import { ExternalLink } from '@/components/external-link'; +import { ExternalFavicon } from '@/components/external-favicon'; + +type TooltipLinkElement = React.ComponentRef; + +interface TooltipLinkProps extends React.ComponentPropsWithoutRef { + href: string; +} + +export const TooltipLink = React.forwardRef( + (props, forwardedRef) => { + const parsedUrl = React.useMemo( + () => props.href.replace(/^(?:https?:\/\/)?(?:www\.)?|\/$/g, ''), + [props.href], + ); + + return ( + +
+ +
+
+
{parsedUrl}
+
+ + } + > + +
+ ); + }, +); + +TooltipLink.displayName = 'TooltipLink'; diff --git a/app/[projectId]/page.tsx b/app/[projectId]/page.tsx new file mode 100644 index 0000000..6aa1258 --- /dev/null +++ b/app/[projectId]/page.tsx @@ -0,0 +1,299 @@ +import * as React from 'react'; +import { notFound } from 'next/navigation'; +import { UserIcon, CalendarIcon } from '@heroicons/react/16/solid'; + +import { projects } from '@/data'; + +import { Container } from '@/components/container'; +import { Line } from '@/components/line'; +import { Hatch } from '@/components/hatch'; +import { Quote } from '@/components/quote'; +import { Gutter } from '@/components/gutter'; +import { Author } from '@/components/author'; +import { WorkItem } from '@/components/work-item'; +import { TeamList } from './_components/team-list'; + +import * as HoverGroup from '@/components/primitives/hover-group'; + +import AragonContent from './_components/content/aragon'; +import BlocksContent from './_components/content/blocks'; +import DashContent from './_components/content/dash'; +import RadixContent from './_components/content/radix'; +import { RouterImage, RouterTransition } from '../router'; +import { Metadata } from 'next'; + +export async function generateMetadata(props: { + params: Promise<{ projectId: string }>; +}): Promise { + const { projectId } = await props.params; + const project = projects.find((project) => project.id === projectId); + + if (!project) return {}; + + return { + title: `Andy Hook – ${project.title}`, + description: project.subtitle, + }; +} + +const projectContent: Record = { + aragon: AragonContent, + blocks: BlocksContent, + dash: DashContent, + radix: RadixContent, +}; + +export async function generateStaticParams() { + return projects.map((project) => ({ + projectId: project.id, + })); +} + +export default async function ProjectPage(props: { params: Promise<{ projectId: string }> }) { + const { projectId } = await props.params; + const project = projects + .filter((project) => !project.externalUrl) + .find((project) => project.id === projectId); + + if (!project) notFound(); + + // Get the appropriate content component for this project + const Content = projectContent[projectId]; + + const projectMeta = [ + [ + UserIcon, + <> + Role: + {project.role} + , + ], + [ + CalendarIcon, + <> + Tenure: + {project.tenure} + , + ], + ] as const; + + const moreProjects = projects.filter((project) => project.id !== projectId); + + const renderedTeam = project.team.map(({ avatar, name, role }) => ({ + avatar, + description: `${name} – ${role}`, + })); + + return ( +
+
+ + +

+
+ + +
{project.title}
+
+
+ + +
+
+ {project.subtitle} +
+
+ + +
+

+
+
+ + + +
+ + +
    + {projectMeta.map(([Icon, text], i) => ( +
  • +
    + +
    +
    + {text} +
    +
  • + ))} +
+ + {renderedTeam.length > 0 ? ( + + ) : null} +
+
+
+
+ + + +
+
+ +
+
+
+ + + +
+
+
+ + + + + +
    + {project.technologies.map((technology) => ( +
  • + {technology} +
  • + ))} +
+
+ +

+ {project.intro} +

+
+
+
+
+
+
+ + + +
+ + + +
+ + + + + + {project.testimonial.full} + + +
+ +
+
+
+
+
+
+ +
+ + + +
+ + + + +
    + {moreProjects.map((project, i) => { + const firstItem = i === 0; + const lastItem = i === moreProjects.length - 1; + + return ( +
  • +
    + {!firstItem && ( + + )} + {!lastItem && ( + + )} +
    + +
    + {!firstItem && } + {!lastItem && } +
    + + +
  • + ); + })} +
+
+
+
+
+
+
+
+ ); +} diff --git a/app/favicon.ico b/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..8e48a708de2a315bd665932d31262e9fb4d565bc GIT binary patch literal 15086 zcmeHNYiv|S6uu26{36}mZp#}MD9EFf@~Rj`YE0A^XwV;pSV&CNSlWUl%A;WGY9eh> zz(fuHz+f;GDkham3HYZ)5NI_)FeH)?TlyfB68ZoVXtzC{Z}!f5_jVum-o5RX=uKwl z-nld9eCL}pGiTr1^K1?3@13 zk_HV!KLq)*#A7z!QM6q`9+W>0HfYy^K?ZQaj-n!4crweYnfiz&B3%Xf`6I%H!h(E4 zTE8xrGI$N1?wKlZ>DPi#w#3^8U=1}^ySlolS+@>^!jH17=a$E{AK>euYzT)}Uk+RU zx50YcDaW)O{QwzKUOe(xZGa!f+XnbDd^*x~q~frcr&e%X%v+eh%F9Zrf@7z6<43QVj>q8l0qpt&dqtt149?k}g zTbpzN`;g%W#!xZ!pI5HfhM)KEwKj1P78B-3{bh#_;p|~J@Pi+{4Ok0o;O)oZJU7?c zV9Q#dek-$q2g6)yZNQ#C7Tq{MjP?B1ihgNBtUiSETbw~Nh9Ox4BH0ETVw{IMJ$&{r z@UtI9(ltWgRRKG$aro97!x?lu+DhN?Tc9W|mt$W%a9GjF<946KEpzyi9cR&2@R(@> zjMDEVcA3MEMzRgS28gyTJw9T*?nuT`9eeQwqGQzyp<2cvNw3coB9 z;{xCL{NKtj&sM2E*2worvDk;UQcf-3*Mev0($6mvk3J_amyn|P@M49cbRkbvlqTe( z6{QaOsDE=Wo)Z*B=6J;Of9>+B28&$7M;*#Fd^O>jd6c|Lo|$qbfq(EnEf1$GPA2}R zl`v+^n51NF748 zN9r@016Mb6X)q)RyZyS2%89jJH*_)w3Fs5>GQ@W4@eXi5H^A567w8*K`Z__+$w@96 zmy%3wmoun5TDu7EyHr0f;X3e-h;q}@=Fv~p)wC!hgMcMkeFN|Ncup4eqJ8gtsgv$O?DH$L-h3Y z1o{H{>n~oU>AXf*;MUX}0Qne;n>TF~JVQ6GJ9qBTD=)uf*6(DjXlVYGwqZ<6nlPTW zZQUaJ9T`L~(nn$iy(zyKaT{>UqMz}RbPZWexzcM@xa96H;04Zs>0 zb^Q$|2R!U9FB3dq9xT|9XOsiz{2ehq=cau?iw8bgR=SJs-@i}Q93xE}KhB8#(22bP zVxXw;msX$t9(-fYE8X=8UB7;vGM6kiq95^B31UE&hy8|;Heu@k19&LfzKxb;E)82x zqo*f@W8aA!10xpZa??Jjk!3`mwhny%vfPhx44%WXu=ml{8OnR?KXS1Dz&;FnA?}OK zDu$ut0kXTHOSAt11c3oOsCyyoN9^8zcA=kPw_djZ;QtZ(G57`V5la}uaWy{2JYjbK z-HiB}{j!o}6~oWrYpf>dRHcbe5$gD4!K*9hdP8=3SyvtFY+~Iwr@+So52q;S6bUJb zi8GR%&LU11R6cr{BfX#Yig(O4_p=vmAR0NBpX2&ygSjt~pWz+qK-M|YmSy3YQ$XLe zsZ)&1wcLg=`^PwB2HL;NYv}si+-NfTlUxCNgD^ky^fJ13?Jp5?AU4s;*4)c=E(kl` zBc{TC%-dR9MO=&ZOw&$#AF?fC-M8O*lMwT@w6sw6%9UbG3?m~T4|CN7Ik(E;sEcl)?xj{o~*L+sNhuB4tD!}GK|jw2jYmEH`@euU3<0voENw} z$bV!#@Ch7y_w3=AXlsCH^*#F!))$PMVE>ivWsX>4@uG$L_Puf(#WH>ZV-VvAW79vM Th2m5lzp&R?B*!nekLmgc2Yh-k literal 0 HcmV?d00001 diff --git a/app/globals.css b/app/globals.css new file mode 100644 index 0000000..e58c987 --- /dev/null +++ b/app/globals.css @@ -0,0 +1,7 @@ +@tailwind base; +@tailwind utilities; + +/* Prevent tailwind breaking 'until-found' element visibility */ +[hidden='until-found'] { + display: revert-layer; +} diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 0000000..202bc61 --- /dev/null +++ b/app/layout.tsx @@ -0,0 +1,339 @@ +import './globals.css'; + +import * as React from 'react'; +import type { Metadata } from 'next'; +import { Manrope, IBM_Plex_Serif } from 'next/font/google'; +import { cx } from '@/cva.config'; + +import { Container } from '@/components/container'; + +import * as Sidebar from './sidebar'; + +import { Line } from '@/components/line'; +import { Hatch } from '@/components/hatch'; +import { Gutter } from '@/components/gutter'; +import { Copy } from '@/components/copy'; + +import * as TooltipPrimitive from '@/components/primitives/tooltip'; +import { SocialLink } from '@/components/social-link'; +import { DeviceProvider } from '@/components/utils/use-device'; +import { Theme } from './theme'; +import { RouterLink, RouterProvider, RouterTransition } from './router'; +import { headers } from 'next/headers'; +import { FocusRing } from '@/components/focus-ring'; + +const displayFont = IBM_Plex_Serif({ + weight: ['300', '400', '500'], + subsets: ['latin'], + display: 'swap', + variable: '--font-display', +}); + +const bodyFont = Manrope({ + subsets: ['latin'], + display: 'swap', + variable: '--font-body', +}); + +export const metadata: Metadata = { + title: 'Andy Hook – Software Engineer', + description: 'Building next-generation user interfaces out of the UK', +}; + +export default async function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + const resolvedHeaders = await headers(); + return ( + + + + + + + + +
+ + + + + + +
+ + {children} + + +
+ + +
+ + + + + + + + + + ); +} + +/* -----------------------------------------------------------------------------------------------*/ + +type HeaderElement = React.ComponentRef<'header'>; + +interface HeaderProps extends React.ComponentPropsWithoutRef<'header'> {} + +const Header = React.forwardRef( + ({ className, ...props }, forwardedRef) => { + return ( +
+ + + + + Andy Hook + + + + +
+ ); + }, +); + +Header.displayName = 'Header'; + +/* -----------------------------------------------------------------------------------------------*/ + +type FooterElement = React.ComponentRef<'footer'>; + +interface FooterProps extends React.ComponentPropsWithoutRef<'footer'> {} + +const Footer = React.forwardRef((props, forwardedRef) => { + return ( +
+ + +
+
+
+
+
+
+
+
+ + + + + +
+ Get in touch +
+ +
+ hello@andyhook.dev +
+ +
+ + +
+ + + +
    + {(['github', 'linkedin', 'dribbble', 'twitter', 'instagram'] as const).map( + (platform) => ( +
  • + +
  • + ), + )} +
+
+
+
+ + +
+ ); +}); + +Footer.displayName = 'Footer'; + +/* -----------------------------------------------------------------------------------------------*/ + +type FooterMarkElement = React.ComponentRef<'svg'>; + +interface FooterMarkProps extends React.ComponentPropsWithoutRef<'svg'> {} + +export const FooterMark = React.forwardRef( + (props, forwardedRef) => { + const uid = React.useId(); + + const verticalGradientId1 = `footer_mark_vertical_gradient_1_${uid}`; + const verticalGradientId2 = `footer_mark_vertical_gradient_2_${uid}`; + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + ); + }, +); + +FooterMark.displayName = 'FooterMark'; + +/* -----------------------------------------------------------------------------------------------*/ + +type BackgroundElement = React.ComponentRef<'div'>; + +interface BackgroundProps extends React.ComponentPropsWithoutRef<'div'> {} + +const Background = React.forwardRef( + ({ className, ...props }, forwardedRef) => { + const uid = React.useId(); + const patternId = `root_background_pattern_${uid}`; + + return ( +
+ + + + + + + + + +
+ + + + + + + + + + +
+ ); + }, +); + +Background.displayName = 'Background'; diff --git a/app/opengraph-image.jpg b/app/opengraph-image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fff39ab478a43b72843ec10be4a017b0bb33b8bc GIT binary patch literal 174673 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
    tbTgKOBQ&>9m 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

    R)l;XAo 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

    ~^Oj?BnMp*8a6I5qoDQiQiEIFUk`Y?gO^&rHqL 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 literal 0 HcmV?d00001 diff --git a/app/router.tsx b/app/router.tsx new file mode 100644 index 0000000..40b523b --- /dev/null +++ b/app/router.tsx @@ -0,0 +1,444 @@ +'use client'; + +import * as React from 'react'; +import NextLink from 'next/link'; +import { useRouter } from 'next/navigation'; +import { AnimatePresence, motion, useInView } from 'motion/react'; +import { projects } from '@/data'; +import { cx } from '@/cva.config'; +import NextImage from 'next/image'; + +import { createContext } from '@/components/utils/create-context'; +import { useComposedRefs } from '@/components/utils/compose-refs'; +import { Line } from '@/components/line'; +import { Hatch } from '@/components/hatch'; +import { Spinner } from '@/components/spinner'; +import { ImageWithMetadata } from '@/types'; + +const GENTLE_TRANSITION = { + type: 'spring', + stiffness: 110, + damping: 20, + mass: 1, +}; + +const SNAPPY_TRANSITION = { + type: 'spring', + stiffness: 250, + damping: 30, + mass: 1, +}; + +type TransitionState = 'initial' | 'idle' | 'cover' | 'loading' | 'enter' | 'intro'; + +/* ------------------------------------------------------------------------------------------------- + * RouterProvider + * -----------------------------------------------------------------------------------------------*/ + +type RouterContextValue = { + onLinkClick: (path: string) => void; + state: TransitionState; + onImageMount: (id: string) => void; + onImageLoad: (id: string) => void; +}; + +const [RouterProviderImpl, useRouterContext] = createContext('Router'); + +const RouterProvider: React.FC = (props) => { + const { children } = props; + const router = useRouter(); + const [state, setState] = React.useState('initial'); + const [imagesReadyMap, setImagesReadyMap] = React.useState | null>(null); + const [navigationPending, startTransition] = React.useTransition(); + const [screenFilled, setScreenFilled] = React.useState(false); + const [path, setPath] = React.useState(''); + + const spinnerVariant = state === 'initial' ? 'dark' : getSpinnerVariantFromPath(path); + const allImagesReady = imagesReadyMap && [...imagesReadyMap.values()].every((value) => value); + + const title = React.useMemo(() => { + const defaultTitle = 'Home'; + if (!path) return defaultTitle; + + const project = projects.find((project) => project.id === path); + return project?.title ?? defaultTitle; + }, [path]); + + React.useEffect(() => { + if (screenFilled && !navigationPending) { + setState('loading'); + setScreenFilled(false); + } + }, [screenFilled, navigationPending]); + + React.useEffect(() => { + if (allImagesReady) { + if (state === 'initial') { + setTimeout(() => setState('intro'), 500); + } + if (state === 'loading') { + setTimeout(() => setState('enter'), 50); + } + + setImagesReadyMap(null); + } + }, [allImagesReady, state]); + + return ( + { + setState('cover'); + setPath(href); + }} + onImageMount={(id) => { + if (state === 'initial' || state === 'loading') { + setImagesReadyMap((prev) => { + const prevMap = new Map(prev ? prev : undefined); + prevMap.set(id, false); + return prevMap; + }); + } + }} + onImageLoad={(id) => { + if (state === 'initial' || state === 'loading') { + setImagesReadyMap((prev) => { + const prevMap = new Map(prev ? prev : undefined); + prevMap.set(id, true); + return prevMap; + }); + } + }} + > +

    + + {(state === 'initial' || state === 'cover') && ( + + + + )} + + + + {state === 'initial' && ( + { + if (definition === 'intro') setState('idle'); + }} + className="fixed inset-0 bg-gradient-to-tl from-slate-1 to-slate-2 z-40 pointer-events-none will-change-motion" + transition={{ duration: 0.6 }} + /> + )} + + + + {(state === 'cover' || state === 'loading') && ( + + { + if (definition === 'cover') { + startTransition(() => router.push(`/${path}`)); + setScreenFilled(true); + } else if (definition === 'enter') { + setPath(''); + setState('idle'); + } + }} + className="absolute inset-0 bg-slate-light-1 overflow-hidden shadow-lg flex items-center justify-center will-change-motion" + transition={SNAPPY_TRANSITION} + > + +
    +
    +
    + {title} + +
    + + + + + + + + + + + + +
    +
    +
    +
    +
    + )} +
    + + ); +}; + +/* ------------------------------------------------------------------------------------------------- + * RouterLink + * -----------------------------------------------------------------------------------------------*/ + +type RouterLinkElement = React.ComponentRef; +type NextLinkProps = React.ComponentPropsWithoutRef; +interface RouterLinkProps extends NextLinkProps { + href: string; +} + +const RouterLink = React.forwardRef((props, forwardedRef) => { + const context = useRouterContext(); + + const isExternal = props.href.startsWith('https://'); + const externalProps = isExternal ? { target: '_blank', rel: 'noopener noreferrer' } : {}; + + return ( + { + props.onClick?.(event); + + if (isExternal) return; + + if (!event.isDefaultPrevented() && !isModifiedEvent(event)) { + event.preventDefault(); + context.onLinkClick(props.href); + } + }} + {...externalProps} + /> + ); +}); + +RouterLink.displayName = 'RouterLink'; + +/* ------------------------------------------------------------------------------------------------- + * RouterTransition + * -----------------------------------------------------------------------------------------------*/ + +type RouterTransitionElement = React.ComponentRef; + +const BASE_DISTANCE = 50; +interface RouterTransitionProps extends React.ComponentPropsWithoutRef { + multiplier: number; +} + +const RouterTransition = React.forwardRef( + (props, forwardedRef) => { + const { multiplier, ...transitionProps } = props; + const ref = React.useRef(null); + const context = useRouterContext(); + const composedRefs = useComposedRefs(forwardedRef, ref); + const isInView = useInView(ref, { once: true }); + + return ( + + ); + }, +); +RouterTransition.displayName = 'RouterTransition'; + +/* ------------------------------------------------------------------------------------------------- + * RouterImage + * -----------------------------------------------------------------------------------------------*/ + +type RouterImageElement = React.ComponentRef; + +interface RouterImageProps + extends Omit, 'src' | 'alt'> { + image: ImageWithMetadata; +} + +const RouterImage = React.forwardRef( + (props, forwardedRef) => { + const { image, className, ...imageProps } = props; + const ref = React.useRef(null); + const id = React.useId(); + const context = useRouterContext(); + const composedRefs = useComposedRefs(forwardedRef, ref); + const [isLoaded, setIsLoaded] = React.useState(false); + const [isInView, setInView] = React.useState(null); + + React.useEffect(() => { + if (ref.current) { + const observer = new IntersectionObserver((entries) => { + const [entry] = entries; + setInView(entry.isIntersecting); + observer.disconnect(); + }); + + observer.observe(ref.current); + return () => observer.disconnect(); + } + }, []); + + const { onImageMount, onImageLoad } = context; + + React.useEffect(() => { + onImageMount(id); + }, [onImageMount, id]); + + // If navigating to the same route, the image will already be in cache + // so onLoad won't fire, so we react to the global loading state as well + // If an image is offset it also won't fire onLoad, so we immediately mark as ready + React.useEffect(() => { + const outOfView = isInView === false; + const isReady = ref.current?.complete || outOfView; + if (isReady) onImageLoad(id); + }, [onImageLoad, id, isInView]); + + return ( +
    + + { + onImageLoad(id); + setIsLoaded(true); + imageProps.onLoad?.(event); + }} + className="select-none object-cover relative" + /> + +
    + ); + }, +); +RouterImage.displayName = 'RouterImage'; + +/* -----------------------------------------------------------------------------------------------*/ + +const getSpinnerVariantFromPath = (path: string) => { + if (path === 'radix') return 'radix'; + if (path === 'aragon') return 'aragon'; + if (path === 'blocks') return 'blocks'; + if (path === 'dash') return 'dash'; + return 'light'; +}; + +const isModifiedEvent = (event: React.MouseEvent) => { + const eventTarget = event.currentTarget; + const target = eventTarget.getAttribute('target'); + return ( + (target && target !== '_self') || + event.metaKey || + event.ctrlKey || + event.shiftKey || + event.altKey || + (event.nativeEvent && event.nativeEvent.which === 2) + ); +}; + +const useRouterState = () => { + const context = useRouterContext(); + return context.state; +}; + +export { RouterProvider, RouterLink, RouterTransition, RouterImage, useRouterState }; diff --git a/app/sidebar.tsx b/app/sidebar.tsx new file mode 100644 index 0000000..de167e6 --- /dev/null +++ b/app/sidebar.tsx @@ -0,0 +1,520 @@ +'use client'; + +import * as React from 'react'; +import * as Floating from '@floating-ui/react'; +import { cva, cx } from '@/cva.config'; + +import * as ScrollArea from '@/components/primitives/scroll-area'; + +import { useLayoutEffect } from '@/components/utils/use-layout-effect'; + +import { AnimatePresence, cubicBezier, motion, useInView } from 'motion/react'; +import { createContext } from '@/components/utils/create-context'; +import { useComposedRefs } from '@/components/utils/compose-refs'; +import { Copy } from '@/components/copy'; +import { SocialLink } from '@/components/social-link'; +import { Line } from '@/components/line'; +import { MouseHover } from '@/components/primitives/mouse-hover'; +import { ProjectId } from '@/types'; +import { getProjectById } from '@/data'; +import { RouterLink, useRouterState } from './router'; +import { useDevice } from '@/components/utils/use-device'; +import { FocusRing } from '@/components/focus-ring'; + +const MOTION_TRANSITION = { + ease: cubicBezier(0.5, 0.2, 0.2, 1), + duration: 0.35, +}; + +function useFloating({ open, onOpenChange }: { open: boolean; onOpenChange(open: boolean): void }) { + const { context, refs } = Floating.useFloating({ + open, + onOpenChange, + }); + + const { setReference, setFloating } = refs; + + const { getReferenceProps, getFloatingProps } = Floating.useInteractions([ + Floating.useClick(context), + Floating.useRole(context), + Floating.useDismiss(context, { outsidePressEvent: 'mousedown' }), + ]); + + return React.useMemo( + () => ({ context, getReferenceProps, getFloatingProps, setReference, setFloating }), + [context, getReferenceProps, getFloatingProps, setReference, setFloating], + ); +} + +/* ------------------------------------------------------------------------------------------------- + * Sidebar + * -----------------------------------------------------------------------------------------------*/ + +type SidebarContextValue = { + open: boolean; + sidebarWidth: number | undefined; + onExitAnimationComplete(): void; + headingId: string; + descriptionId: string; + floating: ReturnType; +}; + +const SIDEBAR_NAME = 'Sidebar'; + +const [SidebarProvider, useSidebarContext] = createContext(SIDEBAR_NAME); + +export const Sidebar = ({ children }: { children: React.ReactNode }) => { + const [open, setOpen] = React.useState(false); + const [sidebarWidth, setSidebarWidth] = React.useState(); + const id = React.useId(); + const floating = useFloating({ open, onOpenChange: setOpen }); + const routerState = useRouterState(); + + // Close sidebar when navigating + React.useEffect(() => { + if (routerState === 'cover') setOpen(false); + }, [routerState]); + + // Match fixed element offset applied by floating ui lockScroll + useLayoutEffect(() => { + if (open) { + const scrollbarWidth = parseInt(document.body.style.paddingRight) || undefined; + setSidebarWidth(open ? scrollbarWidth : undefined); + } + }, [open]); + + return ( + setSidebarWidth(undefined)} + headingId={id} + descriptionId={id} + floating={floating} + > + {children} + + ); +}; + +Sidebar.displayName = 'Sidebar'; + +/* ------------------------------------------------------------------------------------------------- + * SidebarTrigger + * -----------------------------------------------------------------------------------------------*/ + +type SidebarTriggerElement = React.ComponentRef<'button'>; + +interface SidebarTriggerProps extends React.ComponentPropsWithoutRef<'button'> {} + +const SidebarTrigger = React.forwardRef( + ({ className, ...props }, forwardedRef) => { + const context = useSidebarContext(); + const composedRefs = useComposedRefs( + (node) => context.floating.setReference(node), + forwardedRef, + ); + + return ( + + + + ); + }, +); + +SidebarTrigger.displayName = 'SidebarTrigger'; + +/* ------------------------------------------------------------------------------------------------- + * SidebarMenu + * -----------------------------------------------------------------------------------------------*/ + +type SidebarMenuElement = React.ComponentRef<'div'>; + +interface SidebarMenuProps extends React.ComponentPropsWithoutRef<'div'> {} + +const SidebarMenu = React.forwardRef( + ({ className, ...props }, forwardedRef) => { + const context = useSidebarContext(); + const composedRefs = useComposedRefs( + (node) => context.floating.setFloating(node), + forwardedRef, + ); + + return ( + + {context.open && ( + +
    + + +
    + + + + + +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    + )} +
    + ); + }, +); + +SidebarMenu.displayName = 'SidebarMenu'; + +/* -----------------------------------------------------------------------------------------------*/ + +type SidebarMenuContentElement = React.ComponentRef<'div'>; + +interface SidebarMenuContentProps extends React.ComponentPropsWithoutRef<'div'> {} + +const SidebarMenuContent = React.forwardRef( + ({ className, ...props }, forwardedRef) => { + const context = useSidebarContext(); + + return ( +
    +

    + Navigation +

    +

    + Select an item to navigate +

    + +
    +
    +
    +

    + Work +

    + + + {(['radix', 'aragon', 'blocks', 'dash'] as const) + .map((projectId) => getProjectById(projectId)) + .filter((project): project is NonNullable => project !== null) + .map((project) => ( + + + + ))} + +
    +
    + +
    +
    + +

    + Get in touch +

    + +
    + hello@andyhook.dev +
    + +
    + + +
      + {(['github', 'linkedin', 'dribbble', 'twitter', 'instagram'] as const).map( + (platform) => ( +
    • + +
    • + ), + )} +
    +
    +
    +
    +
    +
    + ); + }, +); + +SidebarMenuContent.displayName = 'SidebarMenuContent'; + +/* -----------------------------------------------------------------------------------------------*/ + +const sidebarProjectLinkLine = cva({ + base: 'h-full absolute top-0 right-0 rounded-full bg-gradient-to-tl', + variants: { + projectId: { + radix: 'from-radix-1 to-radix-5', + aragon: 'from-aragon-1 to-aragon-5', + blocks: 'from-blocks-1 to-blocks-5', + dash: 'from-dash-1 to-dash-4', + }, + }, +}); + +type SidebarProjectLinkElement = React.ComponentRef; + +interface SidebarProjectLinkProps + extends Omit, 'href'> { + projectId: ProjectId; + title: string; + path: string; +} + +const SidebarProjectLink = React.forwardRef( + ({ className, path, title, projectId, ...props }, forwardedRef) => { + const [hovered, setHovered] = React.useState(false); + + return ( + + + + + + {title} + + +
    +
    + +
    +
    +
    +
    +
    +
    + ); + }, +); + +SidebarProjectLink.displayName = 'SidebarProjectLink'; + +/* ------------------------------------------------------------------------------------------------- + * SidebarOverlay + * -----------------------------------------------------------------------------------------------*/ + +type SidebarOverlayElement = React.ComponentRef<'div'>; + +interface SidebarOverlayProps extends React.ComponentPropsWithoutRef<'div'> {} + +const SidebarOverlay = React.forwardRef( + (props, forwardedRef) => { + const context = useSidebarContext(); + const device = useDevice(); + + return ( + + {context.open && ( + + { + if (definition === 'hidden') { + context.onExitAnimationComplete(); + } + }} + /> + + )} + + ); + }, +); + +SidebarOverlay.displayName = 'SidebarOverlay'; + +/* ------------------------------------------------------------------------------------------------- + * SidebarAnimation + * -----------------------------------------------------------------------------------------------*/ + +type SidebarAnimationElement = React.ComponentRef; + +interface SidebarAnimationProps extends React.ComponentPropsWithoutRef {} + +const SidebarAnimation = React.forwardRef( + ({ className, ...props }, forwardedRef) => { + const ref = React.useRef(null); + const composedRefs = useComposedRefs(forwardedRef, ref); + const context = useSidebarContext(); + const isInView = useInView(ref); + + return ( + + ); + }, +); + +SidebarAnimation.displayName = 'SidebarAnimation'; + +/* -----------------------------------------------------------------------------------------------*/ + +export const Root = Sidebar; +export const Menu = SidebarMenu; +export const Overlay = SidebarOverlay; +export const Trigger = SidebarTrigger; +export const Animation = SidebarAnimation; diff --git a/app/theme.tsx b/app/theme.tsx new file mode 100644 index 0000000..be5389b --- /dev/null +++ b/app/theme.tsx @@ -0,0 +1,45 @@ +'use client'; + +import * as React from 'react'; +import { usePathname } from 'next/navigation'; + +import { ProjectId } from '@/types'; +import { getProjectById } from '@/data'; + +import { cx } from '@/cva.config'; +import { getThemeColorValues } from '@/theme'; + +/* ------------------------------------------------------------------------------------------------- + * Theme + * -----------------------------------------------------------------------------------------------*/ + +type ThemeElement = React.ComponentRef<'div'>; + +interface ThemeProps extends React.ComponentPropsWithoutRef<'div'> {} + +const Theme = React.forwardRef((props, forwardedRef) => { + const { className, ...themeProps } = props; + const pathname = usePathname(); + + const colorVariables = React.useMemo(() => { + const parsedPathname = pathname.replace('/', '') as ProjectId; + const projectId = getProjectById(parsedPathname)?.id; + + return getThemeColorValues(projectId); + }, [pathname]); + + return ( +
    + ); +}); + +Theme.displayName = 'Theme'; + +/* -----------------------------------------------------------------------------------------------*/ + +export { Theme }; diff --git a/components/About/About.tsx b/components/About/About.tsx deleted file mode 100644 index 1955e63..0000000 --- a/components/About/About.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import React from 'react' -import { inclusiveDown, inclusiveUp } from '../../style/responsive' -import Button from '../Button/Button' -import ExperienceList from '../ExperienceList/ExperienceList' -import Icon from '../Icon/Icon' -import SocialProof from '../SocialProof/SocialProof' -import TextHeading from '../Text/TextHeading' -import TextParagraph from '../Text/TextParagraph' - -function About(): JSX.Element { - return ( - <> -
    -
    - - More than 10 years building for the web - -
    - -
    - - Great user experience is the core driving everything I do, I believe - that close collaboration between design, research and front-end - engineering leads to superior customer experiences within digital - products. - - - - As a specialist in modular design systems and component libraries, I - work to bridge the gap between design and engineering disiplines and - act as a catalyst for fast, iterative design processes within agile - product teams. My technical expertise spans a wealth of front-end - technologies ranging from modern SPA application development to - accessibility optimisation and front-end testing. - -
    - -
    - -
    -
    - - - - - ) -} - -export default About diff --git a/components/AccessibleIcon/AccessibleIcon.tsx b/components/AccessibleIcon/AccessibleIcon.tsx deleted file mode 100644 index 700d0ec..0000000 --- a/components/AccessibleIcon/AccessibleIcon.tsx +++ /dev/null @@ -1,44 +0,0 @@ -// Accessible icon implementation heavily inspired by the fantastic Radix UI team -// https://github.com/radix-ui/primitives/blob/main/packages/react/accessible-icon/src/AccessibleIcon.tsx -import React from 'react' - -type AccessibleIconProps = { - /** - * The accessible label for the icon. This label will be visually hidden but announced to screen - * reader users, similar to `alt` text for `img` tags. - */ - label: string - children: React.ReactNode -} - -function AccessibleIcon({ children, label }: AccessibleIconProps): JSX.Element { - const child = React.Children.only(children) - return ( - <> - {React.cloneElement(child as React.ReactElement, { - // accessibility - 'aria-hidden': 'true', - focusable: 'false', // See: https://allyjs.io/tutorials/focusing-in-svg.html#making-svg-elements-focusable - })} - - {label} - - - ) -} - -export default AccessibleIcon diff --git a/components/Button/Button.tsx b/components/Button/Button.tsx deleted file mode 100644 index cc1a510..0000000 --- a/components/Button/Button.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { motion } from 'framer-motion' -import React from 'react' -import { useTheme } from '../../hooks/useTheme/useTheme' -import { spring } from '../../style/motion' -import { inclusiveUp } from '../../style/responsive' -import { - setCropAndLineHeight, - setTextStyle, - typeScale, -} from '../../style/typography' -import InteractionBase from '../InteractionBase/InteractionBase' - -type ButtonProps = { - href?: string - newTab?: boolean - children: React.ReactNode - onClick?: () => void - icon?: React.ReactNode -} - -function Button({ - href, - children, - newTab, - onClick, - icon, -}: ButtonProps): JSX.Element { - const theme = useTheme() - - const radius = theme.radius.pill - - return ( - - - -
    -
    - {icon && ( -
    -
    -
    - {icon} -
    -
    -
    - )} - - {children} -
    -
    -
    -
    - ) -} - -export default Button diff --git a/components/ExperienceList/ExperienceList.tsx b/components/ExperienceList/ExperienceList.tsx deleted file mode 100644 index 23e07ed..0000000 --- a/components/ExperienceList/ExperienceList.tsx +++ /dev/null @@ -1,233 +0,0 @@ -import React from 'react' -import { ImagePath } from '../../data/images' -import { inclusiveDown, inclusiveUp } from '../../style/responsive' -import Panel from '../Panel/Panel' -import TextBase from '../Text/TextBase' -import TextHeading from '../Text/TextHeading' - -type ExperienceEntry = { - year: string - logoSrc: ImagePath - title: string - company: string -} - -const ENTRIES: ExperienceEntry[] = [ - { - year: '2021', - logoSrc: 'logos/modulz-mark.svg', - title: 'Senior Product Engineer', - company: 'Modulz', - }, - { - year: '2020', - logoSrc: 'logos/aragon-mark.svg', - title: 'Senior UI Engineer', - company: 'Aragon One', - }, - { - year: '2018', - logoSrc: 'logos/bright-mark.svg', - title: 'UI Engineer', - company: 'Bright Interactive', - }, - { - year: '2016', - logoSrc: 'logos/brandwatch-mark.svg', - title: 'Senior Front-End Developer', - company: 'Brandwatch', - }, - { - year: '2012', - logoSrc: 'logos/tjc-mark.svg', - title: 'Front-End Developer', - company: 'Jamieson Consultancy', - }, - { - year: '2009', - logoSrc: 'logos/andyhook-mark.svg', - title: 'Digital Designer', - company: 'Freelance', - }, -] - -function ExperienceList(): JSX.Element { - return ( -
      - {ENTRIES.map((item, i) => ( -
    • - - {/* Employment year */} -
      - - {item.year} - -
      - - {/* Logo mark */} -
      - -
      - - {/* Company name */} -
      - - {item.company} - -
      - - {/* Job title */} -
      - - {item.title} - -
      -
      -
    • - ))} -
    - ) -} - -export default ExperienceList diff --git a/components/Footer/Footer.tsx b/components/Footer/Footer.tsx deleted file mode 100644 index e78d477..0000000 --- a/components/Footer/Footer.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import React from 'react' -import { inclusiveDown, inclusiveUp } from '../../style/responsive' -import { setTextStyle } from '../../style/typography' -import InteractionBase from '../InteractionBase/InteractionBase' -import LayoutGutter from '../Layout/LayoutGutter' -import LayoutLimiter from '../Layout/LayoutLimiter' -import LayoutRow from '../Layout/LayoutRow' -import SocialIcons from '../SocialIcons/SocialIcons' -import GradientText from '../GradientText/GradientText' -import TextHeading from '../Text/TextHeading' -import TextBase from '../Text/TextBase' -import { removeWidow } from '../../style/utils' -import LayoutShade from '../Layout/LayoutShade' -import { META } from '../../data/meta' -import Signature from '../Signature/Signature' -import { useTheme } from '../../hooks/useTheme/useTheme' - -function Footer(): JSX.Element { - const theme = useTheme() - - return ( -
    - - - - -
    - - Let’s build something awesome - - - - Start by{' '} - - - {removeWidow('saying hello')} - - - -
    - - -
    - - -
    -
    -
    -
    - ) -} - -export default Footer diff --git a/components/GradientText/GradientText.tsx b/components/GradientText/GradientText.tsx deleted file mode 100644 index fa2c6b6..0000000 --- a/components/GradientText/GradientText.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from 'react' -import { css, keyframes } from 'styled-components' -import { useTheme } from '../../hooks/useTheme/useTheme' - -const textShimmerAnimation = css` - background-size: 500% 500%; - - animation: ${keyframes` - 0%{ - background-position: 100% 100%; - } - 100%{ - background-position: 0% 0%; - } - `} 3s linear infinite; -` - -type GradientTextProps = { - children: string -} - -function GradientText({ children, ...props }: GradientTextProps): JSX.Element { - const { accent } = useTheme() - - return ( - - {children} - - ) -} - -export default GradientText diff --git a/components/Icon/Icon.tsx b/components/Icon/Icon.tsx deleted file mode 100644 index 512b00e..0000000 --- a/components/Icon/Icon.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react' -import { IconName, ICON_PATHS } from '../../data/icons' - -type IconProps = { - name: IconName -} - -function Icon({ name, ...props }: IconProps): JSX.Element { - const iconPath = ICON_PATHS[name] - - return ( -
    - - - -
    - ) -} - -export default Icon diff --git a/components/ImageBase/ImageBase.tsx b/components/ImageBase/ImageBase.tsx deleted file mode 100644 index dcaf3ac..0000000 --- a/components/ImageBase/ImageBase.tsx +++ /dev/null @@ -1,205 +0,0 @@ -import React, { useCallback, useMemo, useState } from 'react' -import Image from 'next/image' -import { AnimatePresence, motion } from 'framer-motion' -import { BreakpointName, breakpoints } from '../../style/responsive' -import { useTheme } from '../../hooks/useTheme/useTheme' -import { spring } from '../../style/motion' -import { css, keyframes } from 'styled-components' -import { imageData, ImageProperties } from '../../data/images' -import { loadingShimmerGradientFromColor } from '../../style/utils' -import { useTrackLoading } from '../../hooks/useLoadPercentage/useLoadPercentage' - -const shimmerAnimation = css` - background-size: 500% 500%; - - animation: ${keyframes` - 0%{ - background-position: 100% 100%; - opacity: 0.75; - } - 100%{ - background-position: 0% 0%; - opacity: 0; - } - `} 2s linear infinite; -` - -type BackgroundColorPreset = 'light' | 'dark' - -type ImageBaseProps = { - scaleRender?: number - scaleRenderFromBp?: [BreakpointName, number] - quality?: number - backgroundColor?: BackgroundColorPreset | string - onLoad?: () => void - visibleOpacity?: number - loading?: 'eager' | 'lazy' -} & ImageProperties - -function ImageBase({ - imagePath, - alt, - backgroundColor = 'dark', - scaleRender = 100, - quality = 100, - scaleRenderFromBp, - onLoad, - visibleOpacity = 1, - loading = 'eager', - ...props -}: ImageBaseProps): JSX.Element { - const { background, foreground } = useTheme() - const [imageLoaded, setImageLoaded] = useState(false) - const { trackLoaded } = useTrackLoading(imagePath) - - const image = imageData[imagePath] - - // There is no need to optimise and render srcset for a scalable SVG - const unoptimized = useMemo(() => imagePath.endsWith('.svg'), [imagePath]) - - const sizesMediaString = useMemo(() => { - if (!scaleRenderFromBp) { - return `${scaleRender}vw` - } - - const [breakpointName, breakpointScaleValue] = scaleRenderFromBp - - return `(min-width: ${breakpoints[breakpointName]}) ${breakpointScaleValue}vw, ${scaleRender}vw` - }, [scaleRender, scaleRenderFromBp]) - - const fireLoadEvents = useCallback(() => { - if (!imageLoaded) { - setImageLoaded(true) - trackLoaded() - onLoad && onLoad() - } - }, [onLoad, trackLoaded, imageLoaded]) - - // When using loading strategy "eager", next/image won't reliably fire onLoad events when retrieving from client cache - // To work around this we need to check the "complete" property on the image element and run the same set of callbacks - const handleLoadFromCache = useCallback( - (wrapperRef) => { - // We are unable to set a ref to the underlying image element directly so we must access it via querySelector on the wrapper - // We are querying by srcset attribute to differentiate from the placeholder image that next/image adds to reserve space - // https://github.com/vercel/next.js/discussions/18386 - const image = wrapperRef?.querySelector('img[srcset]') as HTMLImageElement - - if (image && image.complete) { - fireLoadEvents() - } - }, - [fireLoadEvents] - ) - - const handleLoadEvent = useCallback( - (e) => { - // The next/image placeholder image triggers a duplicate event - // We only want to trigger the load handler when the actual image is loaded, hence making sure the source of the target element triggering the event is not base64. - // See https://github.com/vercel/next.js/issues/20368#issuecomment-757446007 - if (e.target.src.indexOf('data:image/gif;base64') < 0) { - fireLoadEvents() - } - }, - [fireLoadEvents] - ) - - const { gradientStop, gradientStopAlpha, sourceColor } = useMemo(() => { - const backgroundPreset: Record = { - light: foreground('extraHigh'), - dark: background('medium'), - } - - const isPreset = backgroundColor === 'light' || backgroundColor === 'dark' - - return loadingShimmerGradientFromColor( - isPreset - ? backgroundPreset[backgroundColor as BackgroundColorPreset] - : backgroundColor - ) - }, [background, foreground, backgroundColor]) - - return ( -
    - - {!imageLoaded && ( - -
    - - )} - - - {alt} - -
    - ) -} - -export default ImageBase diff --git a/components/InteractionBase/InteractionBase.spec.tsx b/components/InteractionBase/InteractionBase.spec.tsx deleted file mode 100644 index 848cbbd..0000000 --- a/components/InteractionBase/InteractionBase.spec.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import React from 'react' -import { axe } from 'jest-axe' -import type { RenderResult } from '@testing-library/react' -import { render, fireEvent } from '../../utils/testing' -import InteractionBase from './InteractionBase' - -const BUTTON_TEXT = 'Button text' -const EXTERNAL_ATTRIBUTES = [ - ['rel', 'noopener noreferrer'], - ['target', '_blank'], -] - -describe('given a default InteractionBase', () => { - let rendered: RenderResult - let element: HTMLElement | null - const handleClick = jest.fn() - - beforeEach(() => { - rendered = render( - {BUTTON_TEXT} - ) - }) - - it('should have no accessibility violations', async () => { - expect(await axe(rendered.container)).toHaveNoViolations() - }) - - it('should render as a button element', () => { - element = rendered.queryByRole('button') - - expect(element).toBeInTheDocument() - }) - - it('should call onClick when clicked', () => { - fireEvent.click(rendered.getByText(BUTTON_TEXT)) - expect(handleClick).toHaveBeenCalledTimes(1) - }) - - it('should not call onClick when disabled', () => { - handleClick.mockReset() - - rendered.rerender( - - {BUTTON_TEXT} - - ) - - fireEvent.click(rendered.getByText(BUTTON_TEXT)) - - expect(handleClick).not.toBeCalled() - }) - - it('should apply outline offsets correctly', () => { - element = rendered.getByText(BUTTON_TEXT) - - fireEvent.focus(element) - - rendered.rerender( - {BUTTON_TEXT} - ) - - const outlineElement = element.querySelector('span') - - expect(outlineElement).toHaveStyleRule('top', '-1em') - expect(outlineElement).toHaveStyleRule('bottom', '-1em') - expect(outlineElement).toHaveStyleRule('left', '-1em') - expect(outlineElement).toHaveStyleRule('right', '-1em') - - rendered.rerender( - {BUTTON_TEXT} - ) - - expect(outlineElement).toHaveStyleRule('top', '-1em') - expect(outlineElement).toHaveStyleRule('bottom', '-1em') - expect(outlineElement).toHaveStyleRule('left', '-0.5em') - expect(outlineElement).toHaveStyleRule('right', '-0.5em') - }) -}) - -describe('given an InteractionBase with external href', () => { - let rendered: RenderResult - let element: HTMLElement | null - - beforeEach(() => { - rendered = render( - {BUTTON_TEXT} - ) - }) - - it('should have no accessibility violations', async () => { - expect(await axe(rendered.container)).toHaveNoViolations() - }) - - it('should render as a link element', () => { - element = rendered.queryByRole('link') - - expect(element).toBeInTheDocument() - }) - - it('should apply correct attributes', () => { - element = rendered.queryByRole('link') - - EXTERNAL_ATTRIBUTES.forEach(([attribute, property]) => { - expect(element).toHaveAttribute(attribute, property) - }) - }) - - it('should render as a button and not apply external attributes when disabled', () => { - rendered.rerender( - - {BUTTON_TEXT} - - ) - - element = rendered.queryByRole('button') - - EXTERNAL_ATTRIBUTES.forEach(([attribute, property]) => { - expect(element).not.toHaveAttribute(attribute, property) - }) - }) -}) - -describe('given an InteractionBase with internal href', () => { - let rendered: RenderResult - let element: HTMLElement | null - - beforeEach(() => { - rendered = render( - {BUTTON_TEXT} - ) - }) - - it('should render as a link element', () => { - element = rendered.queryByRole('link') - - expect(element).toBeInTheDocument() - }) - - it('should only apply external attributes when passed newTab', () => { - element = rendered.queryByRole('link') - - EXTERNAL_ATTRIBUTES.forEach(([attribute, property]) => { - expect(element).not.toHaveAttribute(attribute, property) - }) - - rendered.rerender( - - {BUTTON_TEXT} - - ) - - EXTERNAL_ATTRIBUTES.forEach(([attribute, property]) => { - expect(element).toHaveAttribute(attribute, property) - }) - }) -}) diff --git a/components/InteractionBase/InteractionBase.tsx b/components/InteractionBase/InteractionBase.tsx deleted file mode 100644 index ef8a48a..0000000 --- a/components/InteractionBase/InteractionBase.tsx +++ /dev/null @@ -1,178 +0,0 @@ -import { AnimatePresence, motion } from 'framer-motion' -import React, { useCallback, useMemo } from 'react' -import styled from 'styled-components' -import { useFocusVisible } from '../../hooks/useFocusVisible/useFocusVisible' -import { useTheme } from '../../hooks/useTheme/useTheme' -import { useRouter } from 'next/router' -import { isExternalURL, noop } from '../../utils/general' -import { Theme } from '../../style/theme' -import { spring } from '../../style/motion' - -type InteractionBaseProps = { - children: React.ReactNode - href?: string - disabled?: boolean - offset?: number | [number, number] - radius?: keyof Theme['radius'] - newTab?: boolean - onClick?: (event: React.MouseEvent) => void -} - -type ElementProps = [ - 'button' | 'a', - { - href?: string - rel?: string - target?: string - disabled?: boolean - } -] - -function getLinkProps(href: string, newTab: boolean): ElementProps { - const external = isExternalURL(href) || newTab - - const externalProps = external - ? { - rel: 'noopener noreferrer', - target: '_blank', - } - : {} - - return [ - 'a', - { - href: href, - ...externalProps, - }, - ] -} - -function getButtonProps(disabled: boolean): ElementProps { - return [ - 'button', - { - disabled, - }, - ] -} - -function isModifiedEvent(event: React.MouseEvent): boolean { - const { target } = event.currentTarget as HTMLAnchorElement - return ( - (target && target !== '_self') || - event.metaKey || - event.ctrlKey || - event.shiftKey || - event.altKey || // triggers resource download - (event.nativeEvent && event.nativeEvent.which === 2) - ) -} - -function InteractionBase({ - children, - href, - onClick, - disabled = false, - offset = 0, - radius = 'base', - newTab = false, - ...props -}: InteractionBaseProps): JSX.Element { - const router = useRouter() - const { focusVisible, onFocus, onBlur } = useFocusVisible() - const theme = useTheme() - - const handleOnClick = useCallback( - (event: React.MouseEvent) => { - if (onClick) { - onClick(event) - } - - if (isModifiedEvent(event)) { - return - } - - // Use internal router for all relative links - if (href && !isExternalURL(href)) { - event.preventDefault() - router.push(href) - } - }, - [onClick, href, router] - ) - - const [elementTag, elementProps] = - href && !disabled ? getLinkProps(href, newTab) : getButtonProps(disabled) - - const [offsetX, offsetY] = useMemo(() => { - if (typeof offset === 'number') { - return [offset, offset] - } - - return offset - }, [offset]) - - return ( - - {children} - - {focusVisible && ( - - )} - - - ) -} - -const StyledInteractiveElement = styled.button` - position: relative; -` - -export default InteractionBase diff --git a/components/Layout/LayoutGutter.tsx b/components/Layout/LayoutGutter.tsx deleted file mode 100644 index ac0b6f0..0000000 --- a/components/Layout/LayoutGutter.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react' -import { inclusiveUp } from '../../style/responsive' - -type LayoutGutterProps = { - children: React.ReactNode -} - -function LayoutGutter({ children, ...props }: LayoutGutterProps): JSX.Element { - return ( -
    - {children} -
    - ) -} - -export default LayoutGutter diff --git a/components/Layout/LayoutLimiter.tsx b/components/Layout/LayoutLimiter.tsx deleted file mode 100644 index 8e61753..0000000 --- a/components/Layout/LayoutLimiter.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react' -import { rem } from 'polished' -import { useTheme } from '../../hooks/useTheme/useTheme' - -type LimiterWidth = 'small' | 'medium' | 'large' - -interface LayoutLimiterProps { - size?: 'small' | 'medium' | 'large' - children: React.ReactNode - divider?: boolean -} - -const LIMITER_WIDTHS: Record = { - small: rem('900px'), - medium: rem('1550px'), - large: rem('1900px'), -} - -function LayoutLimiter({ - size = 'medium', - divider, - children, - ...props -}: LayoutLimiterProps): JSX.Element { - const theme = useTheme() - const width = LIMITER_WIDTHS[size] - - return ( -
    - {children} -
    - ) -} - -export default LayoutLimiter diff --git a/components/Layout/LayoutRow.tsx b/components/Layout/LayoutRow.tsx deleted file mode 100644 index 7a89ec3..0000000 --- a/components/Layout/LayoutRow.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from 'react' -import { css } from 'styled-components' -import { inclusiveUp } from '../../style/responsive' - -type LayoutRowProps = { - trimTop?: boolean - trimBottom?: boolean - children: React.ReactNode -} - -function LayoutRow({ - children, - trimTop, - trimBottom, - ...props -}: LayoutRowProps): JSX.Element { - return ( -
    - {children} -
    - ) -} - -const topSpaceStyle = css` - padding-top: 4rem; - - ${inclusiveUp('sm')} { - padding-top: 7rem; - } - - ${inclusiveUp('md')} { - padding-top: 10rem; - } - - ${inclusiveUp('lg')} { - padding-top: 12rem; - } -` - -const bottomSpaceStyle = css` - padding-bottom: 4rem; - - ${inclusiveUp('sm')} { - padding-bottom: 7rem; - } - - ${inclusiveUp('md')} { - padding-bottom: 10rem; - } - - ${inclusiveUp('lg')} { - padding-bottom: 12rem; - } -` - -export default LayoutRow diff --git a/components/Layout/LayoutShade.tsx b/components/Layout/LayoutShade.tsx deleted file mode 100644 index 9ed3426..0000000 --- a/components/Layout/LayoutShade.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react' -import { useTheme } from '../../hooks/useTheme/useTheme' - -type LayoutShadeProps = { - children: React.ReactNode - borderTop?: boolean - borderBottom?: boolean -} - -function LayoutShade({ - children, - borderTop, - borderBottom, - ...props -}: LayoutShadeProps): JSX.Element { - const theme = useTheme() - - const borderStyle = ` - ${theme.borderWidth.regular} solid - ${theme.background('extraHigh')} - ` - - return ( -
    - {children} -
    - ) -} - -export default LayoutShade diff --git a/components/Link/Link.tsx b/components/Link/Link.tsx deleted file mode 100644 index eb60805..0000000 --- a/components/Link/Link.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react' -import { useTheme } from '../../hooks/useTheme/useTheme' -import InteractionBase from '../InteractionBase/InteractionBase' - -type LinkProps = { - children: React.ReactNode - href: string -} - -function Link({ children, href }: LinkProps): JSX.Element { - const theme = useTheme() - - return ( - - {children} - - ) -} - -export default Link diff --git a/components/LoadingIndicator/LoadingIndicator.tsx b/components/LoadingIndicator/LoadingIndicator.tsx deleted file mode 100644 index b170e69..0000000 --- a/components/LoadingIndicator/LoadingIndicator.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import React, { useEffect, useState } from 'react' -import { useRouter } from 'next/router' -import { motion } from 'framer-motion' -import { spring } from '../../style/motion' -import { useTheme } from '../../hooks/useTheme/useTheme' -import { setLightness } from 'polished' -import { useLoadPercentage } from '../../hooks/useLoadPercentage/useLoadPercentage' - -function LoadingIndicator(): JSX.Element { - const [loadComplete, setLoadComplete] = useState(false) - const [hidden, setHidden] = useState(false) - const router = useRouter() - const theme = useTheme() - const percentLoaded = useLoadPercentage() - - useEffect(() => { - if (percentLoaded === 100) { - setLoadComplete(true) - } - }, [percentLoaded]) - - // We only fill the bar half the way using the loaded percentage - // This let's us rapidly fill the remainder once everything has loaded - // This improves perceived performance - // https://developer.mozilla.org/en-US/docs/Learn/Performance/Perceived_performance - const barPosition = loadComplete ? 0 : -100 + percentLoaded / 2 - - return ( -
    +
    + + + +
    + {hovered && ( + + + + )} + + + {!hovered && ( + + + + )} + +
    + { + if (variant === 'hover') { + setTimeout(() => { + scrambleTextRef.current?.replay(); + }, 70); + } + }} + > + + Download full CV + +
    Download full CV
    +
    +
    +
    +
    +
    + ); + }, +); + +DownloadButton.displayName = 'DownloadButton'; diff --git a/components/external-favicon.tsx b/components/external-favicon.tsx new file mode 100644 index 0000000..8b3f400 --- /dev/null +++ b/components/external-favicon.tsx @@ -0,0 +1,54 @@ +'use client'; + +import * as React from 'react'; + +import { useLayoutEffect } from '@/components/utils/use-layout-effect'; +import { RouterImage } from '@/app/router'; + +type ImageLoadingStatus = 'idle' | 'loading' | 'loaded' | 'error'; + +type ExternalFaviconElement = React.ComponentRef; + +interface ExternalFaviconProps extends React.ComponentPropsWithoutRef {} + +export const ExternalFavicon = React.forwardRef( + ({ image: imageProp, ...props }, forwardedRef) => { + const imageSrc = `https://www.google.com/s2/favicons?domain=${imageProp.src}`; + const imageLoadingStatus = useImageLoadingStatus(imageSrc); + + const image = { ...imageProp, src: imageSrc }; + + return imageLoadingStatus === 'loaded' ? ( + + ) : null; + }, +); + +ExternalFavicon.displayName = 'ExternalFavicon'; + +/* -----------------------------------------------------------------------------------------------*/ + +function useImageLoadingStatus(src: string) { + const [loadingStatus, setLoadingStatus] = React.useState('idle'); + + useLayoutEffect(() => { + let isMounted = true; + const image = new window.Image(); + + const updateStatus = (status: ImageLoadingStatus) => () => { + if (!isMounted) return; + setLoadingStatus(status); + }; + + setLoadingStatus('loading'); + image.onload = updateStatus('loaded'); + image.onerror = updateStatus('error'); + image.src = src; + + return () => { + isMounted = false; + }; + }, [src]); + + return loadingStatus; +} diff --git a/components/external-link.tsx b/components/external-link.tsx new file mode 100644 index 0000000..b35e308 --- /dev/null +++ b/components/external-link.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; +import { cx } from '@/cva.config'; +import { FocusRing } from './focus-ring'; + +type ExternalLinkElement = React.ComponentRef<'a'>; + +interface ExternalLinkProps extends React.ComponentPropsWithoutRef<'a'> {} + +export const ExternalLink = React.forwardRef( + ({ children, className, ...props }, forwardedRef) => { + return ( + +
    + {children} + + + ); + }, +); + +ExternalLink.displayName = 'ExternalLink'; diff --git a/components/focus-ring.tsx b/components/focus-ring.tsx new file mode 100644 index 0000000..5376650 --- /dev/null +++ b/components/focus-ring.tsx @@ -0,0 +1,33 @@ +import { cx } from '@/cva.config'; +import * as React from 'react'; +import { Primitive } from '@/components/primitives/primitive'; + +type FocusRingElement = React.ComponentRef; + +interface FocusRingProps + extends Omit, 'asChild'> { + scheme?: 'light' | 'dark'; +} + +export const FocusRing = React.forwardRef( + (props, forwardedRef) => { + const { scheme = 'dark', ...focusRingsProps } = props; + + const isDarkScheme = scheme === 'dark'; + + return ( + + ); + }, +); + +FocusRing.displayName = 'FocusRing'; diff --git a/components/gutter.tsx b/components/gutter.tsx new file mode 100644 index 0000000..95862a0 --- /dev/null +++ b/components/gutter.tsx @@ -0,0 +1,38 @@ +import * as React from 'react'; +import { cva, VariantProps } from '@/cva.config'; + +const gutter = cva({ + variants: { + size: { + small: 'lg:px-4', + normal: 'md:px-7 lg:px-8', + }, + collapse: { + true: '', + false: '', + }, + }, + compoundVariants: [ + { + size: 'normal', + collapse: false, + className: 'px-5', + }, + ], +}); + +type GutterElement = React.ComponentRef<'div'>; + +interface GutterProps extends React.ComponentPropsWithoutRef<'div'>, VariantProps {} + +export const Gutter = React.forwardRef( + ({ children, className, size = 'normal', collapse = false, ...props }, forwardedRef) => { + return ( +
    + {children} +
    + ); + }, +); + +Gutter.displayName = 'Gutter'; diff --git a/components/hatch.tsx b/components/hatch.tsx new file mode 100644 index 0000000..2fe46b2 --- /dev/null +++ b/components/hatch.tsx @@ -0,0 +1,56 @@ +import * as React from 'react'; +import { cx } from '@/cva.config'; +import { getColorSlateDark, getColorSlateLight } from '@/theme'; + +/* ------------------------------------------------------------------------------------------------- + * Hatch + * -----------------------------------------------------------------------------------------------*/ + +type HatchElement = React.ComponentRef<'div'>; + +interface HatchProps extends React.ComponentPropsWithoutRef<'div'> { + orientation?: 'horizontal' | 'vertical'; + contrast?: 'low' | 'normal'; + scheme?: 'light' | 'dark'; + space?: number; + width?: number; +} + +const Hatch = React.forwardRef( + ( + { + orientation = 'horizontal', + contrast = 'normal', + scheme = 'dark', + space = 18, + width = 1, + ...props + }, + forwardedRef, + ) => { + const vertical = orientation === 'vertical'; + const hatchColor = getHatchColor(scheme, contrast); + + return ( +
    +
    +
    + ); + }, +); + +Hatch.displayName = 'Hatch'; + +/* ---------------------------------------------------------------------------------------------- */ + +const getHatchColor = (scheme: 'light' | 'dark', contrast: 'low' | 'normal') => { + if (scheme === 'light') return contrast === 'low' ? getColorSlateLight(4) : getColorSlateLight(6); + return contrast === 'low' ? getColorSlateDark(2) : getColorSlateDark(3); +}; + +export { Hatch }; diff --git a/components/icons/dribbble-icon.tsx b/components/icons/dribbble-icon.tsx new file mode 100644 index 0000000..29f5120 --- /dev/null +++ b/components/icons/dribbble-icon.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; + +type DribbbleIconElement = React.ComponentRef<'svg'>; + +interface DribbbleIconProps extends React.ComponentPropsWithoutRef<'svg'> {} + +export const DribbbleIcon = React.forwardRef( + (props, forwardedRef) => { + return ( + + + + ); + }, +); + +DribbbleIcon.displayName = 'DribbbleIcon'; diff --git a/components/icons/github-icon.tsx b/components/icons/github-icon.tsx new file mode 100644 index 0000000..b391d7f --- /dev/null +++ b/components/icons/github-icon.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; + +type GithubIconElement = React.ComponentRef<'svg'>; + +interface GithubIconProps extends React.ComponentPropsWithoutRef<'svg'> {} + +export const GithubIcon = React.forwardRef( + (props, forwardedRef) => { + return ( + + + + ); + }, +); + +GithubIcon.displayName = 'GithubIcon'; diff --git a/components/icons/instagram-icon.tsx b/components/icons/instagram-icon.tsx new file mode 100644 index 0000000..5a04426 --- /dev/null +++ b/components/icons/instagram-icon.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; + +type InstagramIconElement = React.ComponentRef<'svg'>; + +interface InstagramIconProps extends React.ComponentPropsWithoutRef<'svg'> {} + +export const InstagramIcon = React.forwardRef( + (props, forwardedRef) => { + return ( + + + + ); + }, +); + +InstagramIcon.displayName = 'InstagramIcon'; diff --git a/components/icons/linkedin-icon.tsx b/components/icons/linkedin-icon.tsx new file mode 100644 index 0000000..76a7a1d --- /dev/null +++ b/components/icons/linkedin-icon.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; + +type LinkedinIconElement = React.ComponentRef<'svg'>; + +interface LinkedinIconProps extends React.ComponentPropsWithoutRef<'svg'> {} + +export const LinkedinIcon = React.forwardRef( + (props, forwardedRef) => { + return ( + + + + ); + }, +); + +LinkedinIcon.displayName = 'LinkedinIcon'; diff --git a/components/icons/twitter-icon.tsx b/components/icons/twitter-icon.tsx new file mode 100644 index 0000000..03745fd --- /dev/null +++ b/components/icons/twitter-icon.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; + +type TwitterIconElement = React.ComponentRef<'svg'>; + +interface TwitterIconProps extends React.ComponentPropsWithoutRef<'svg'> {} + +export const TwitterIcon = React.forwardRef( + (props, forwardedRef) => { + return ( + + + + ); + }, +); + +TwitterIcon.displayName = 'TwitterIcon'; diff --git a/components/line.tsx b/components/line.tsx new file mode 100644 index 0000000..4edaf03 --- /dev/null +++ b/components/line.tsx @@ -0,0 +1,67 @@ +import { cva, VariantProps } from '@/cva.config'; +import * as React from 'react'; + +const line = cva({ + variants: { + contrast: { + low: 'stroke-slate-3', + normal: 'stroke-slate-4', + }, + scheme: { + light: null, + dark: null, + }, + }, + compoundVariants: [ + { + contrast: 'low', + scheme: 'light', + class: 'stroke-slate-light-1/20', + }, + { + contrast: 'normal', + scheme: 'light', + class: 'stroke-slate-light-5', + }, + ], +}); + +type LineElement = React.ComponentRef<'div'>; + +interface LineProps extends React.ComponentPropsWithoutRef<'div'>, VariantProps { + orientation?: 'horizontal' | 'vertical'; + solid?: boolean; +} + +export const Line = React.forwardRef( + ( + { orientation = 'horizontal', solid, contrast = 'normal', scheme = 'dark', ...props }, + forwardedRef, + ) => { + const vertical = orientation === 'vertical'; + + return ( +
    + + + +
    + ); + }, +); + +Line.displayName = 'Line'; diff --git a/components/logos/aragon-mark.tsx b/components/logos/aragon-mark.tsx new file mode 100644 index 0000000..46719e6 --- /dev/null +++ b/components/logos/aragon-mark.tsx @@ -0,0 +1,86 @@ +import * as React from 'react'; + +type AragonMarkElement = React.ComponentRef<'svg'>; + +interface AragonMarkProps extends React.ComponentPropsWithoutRef<'svg'> {} + +export const AragonMark = React.forwardRef( + (props, forwardedRef) => { + const uid = React.useId(); + + const aragonGradientId1 = `aragon_gradient_1_${uid}`; + const aragonGradientId2 = `aragon_gradient_2_${uid}`; + const aragonGradientId3 = `aragon_gradient_3_${uid}`; + const aragonGradientId4 = `aragon_gradient_3_${uid}`; + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + ); + }, +); + +AragonMark.displayName = 'AragonMark'; diff --git a/components/logos/brandwatch-mark.tsx b/components/logos/brandwatch-mark.tsx new file mode 100644 index 0000000..a03c073 --- /dev/null +++ b/components/logos/brandwatch-mark.tsx @@ -0,0 +1,52 @@ +import * as React from 'react'; + +type BrandwatchMarkElement = React.ComponentRef<'svg'>; + +interface BrandwatchMarkProps extends React.ComponentPropsWithoutRef<'svg'> {} + +export const BrandwatchMark = React.forwardRef( + (props, forwardedRef) => { + return ( + + + + + + + + + ); + }, +); + +BrandwatchMark.displayName = 'BrandwatchMark'; diff --git a/components/logos/bright-mark.tsx b/components/logos/bright-mark.tsx new file mode 100644 index 0000000..78b86b0 --- /dev/null +++ b/components/logos/bright-mark.tsx @@ -0,0 +1,39 @@ +import * as React from 'react'; + +type BrightMarkElement = React.ComponentRef<'svg'>; + +interface BrightMarkProps extends React.ComponentPropsWithoutRef<'svg'> {} + +export const BrightMark = React.forwardRef( + (props, forwardedRef) => { + const uid = React.useId(); + + const brightGradientId = `bright_gradient_1_${uid}`; + + return ( + + + + + + + + + + ); + }, +); + +BrightMark.displayName = 'BrightMark'; diff --git a/components/logos/freelance-mark.tsx b/components/logos/freelance-mark.tsx new file mode 100644 index 0000000..bb643b7 --- /dev/null +++ b/components/logos/freelance-mark.tsx @@ -0,0 +1,43 @@ +import * as React from 'react'; + +type FreelanceMarkElement = React.ComponentRef<'svg'>; + +interface FreelanceMarkProps extends React.ComponentPropsWithoutRef<'svg'> {} + +export const FreelanceMark = React.forwardRef( + (props, forwardedRef) => { + const uid = React.useId(); + + const freelanceGradientId = `freelance_gradient_${uid}`; + + return ( + + + + + + + + + + + ); + }, +); + +FreelanceMark.displayName = 'FreelanceMark'; diff --git a/components/logos/modulz-mark.tsx b/components/logos/modulz-mark.tsx new file mode 100644 index 0000000..d01dddf --- /dev/null +++ b/components/logos/modulz-mark.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; + +type ModulzMarkElement = React.ComponentRef<'svg'>; + +interface ModulzMarkProps extends React.ComponentPropsWithoutRef<'svg'> {} + +export const ModulzMark = React.forwardRef( + (props, forwardedRef) => { + return ( + + + + ); + }, +); + +ModulzMark.displayName = 'ModulzMark'; diff --git a/components/logos/scroll-mark.tsx b/components/logos/scroll-mark.tsx new file mode 100644 index 0000000..1d45db5 --- /dev/null +++ b/components/logos/scroll-mark.tsx @@ -0,0 +1,35 @@ +import * as React from 'react'; + +type ScrollMarkElement = React.ComponentRef<'svg'>; + +interface ScrollMarkProps extends React.ComponentPropsWithoutRef<'svg'> {} + +export const ScrollMark = React.forwardRef( + (props, forwardedRef) => { + return ( + + + + + + + + + + + + + + + + ); + }, +); + +ScrollMark.displayName = 'ScrollMark'; diff --git a/components/logos/tjc-mark.tsx b/components/logos/tjc-mark.tsx new file mode 100644 index 0000000..14207b2 --- /dev/null +++ b/components/logos/tjc-mark.tsx @@ -0,0 +1,51 @@ +import * as React from 'react'; + +type TjcMarkElement = React.ComponentRef<'svg'>; + +interface TjcMarkProps extends React.ComponentPropsWithoutRef<'svg'> {} + +export const TjcMark = React.forwardRef((props, forwardedRef) => { + const uid = React.useId(); + + const tjcGradientId1 = `tjc_gradient_1_${uid}`; + const tjcGradientId2 = `tjc_gradient_2_${uid}`; + + return ( + + + + + + + + + + + + + + + + ); +}); + +TjcMark.displayName = 'TjcMark'; diff --git a/components/logos/workos-mark.tsx b/components/logos/workos-mark.tsx new file mode 100644 index 0000000..f969f53 --- /dev/null +++ b/components/logos/workos-mark.tsx @@ -0,0 +1,59 @@ +import * as React from 'react'; + +type WorkosMarkElement = React.ComponentRef<'svg'>; + +interface WorkosMarkProps extends React.ComponentPropsWithoutRef<'svg'> {} + +export const WorkosMark = React.forwardRef( + (props, forwardedRef) => { + const uid = React.useId(); + + const workosGradientId1 = `workos_gradient_1_${uid}`; + const workosGradientId2 = `workos_gradient_2_${uid}`; + + return ( + + + + + + + + + + + + + + + + + ); + }, +); + +WorkosMark.displayName = 'WorkosMark'; diff --git a/components/primitives/accordion.tsx b/components/primitives/accordion.tsx new file mode 100644 index 0000000..99a7778 --- /dev/null +++ b/components/primitives/accordion.tsx @@ -0,0 +1,259 @@ +'use client'; + +import React from 'react'; +import { createContext } from '@/components/utils/create-context'; + +import * as CollapsiblePrimitive from '@/components/primitives/collapsible'; +import { useComposedRefs } from '@/components/utils/compose-refs'; +import { createCollection } from '@/components/primitives/collection'; +import { useControllableState } from '@/components/utils/use-controllable-state'; +import { Primitive } from '@/components/primitives/primitive'; + +/* ------------------------------------------------------------------------------------------------- + * Accordion + * -----------------------------------------------------------------------------------------------*/ + +const ACCORDION_KEYS = ['Home', 'End', 'ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight']; + +const [Collection, useCollection] = createCollection('Accordion'); + +type AccordionValueContextValue = { + value: string[]; + onItemOpen(value: string): void; + onItemClose(value: string): void; +}; + +const [AccordionValueProvider, useAccordionValueContext] = + createContext('Accordion'); + +type AccordionElement = AccordionImplElement; +interface AccordionProps extends AccordionImplProps { + /** + * The controlled stateful value of the accordion item whose content is expanded. + */ + value?: string; + /** + * The value of the item whose content is expanded when the accordion is initially rendered. Use + * `defaultValue` if you do not need to control the state of an accordion. + */ + defaultValue?: string; + /** + * The callback that fires when the state of the accordion changes. + */ + onValueChange?(value: string): void; +} + +const Accordion = React.forwardRef((props, forwardedRef) => { + const { + value: valueProp, + defaultValue, + onValueChange = () => {}, + ...accordionSingleProps + } = props; + + const [value, setValue] = useControllableState({ + prop: valueProp, + defaultProp: defaultValue, + onChange: onValueChange, + }); + + return ( + + setValue('')} + > + + + + ); +}); + +Accordion.displayName = 'Accordion'; + +/* -----------------------------------------------------------------------------------------------*/ + +type AccordionImplElement = React.ComponentRef; +type PrimitiveDivProps = React.ComponentPropsWithoutRef; +interface AccordionImplProps extends PrimitiveDivProps {} + +const AccordionImpl = React.forwardRef( + (props, forwardedRef) => { + const accordionRef = React.useRef(null); + const composedRefs = useComposedRefs(accordionRef, forwardedRef); + const getItems = useCollection(); + + const handleKeyDown = (event: React.KeyboardEvent) => { + if (!ACCORDION_KEYS.includes(event.key)) return; + const target = event.target as HTMLElement; + const triggerCollection = getItems(); + const triggerIndex = triggerCollection.findIndex((item) => item.ref.current === target); + const triggerCount = triggerCollection.length; + + if (triggerIndex === -1) return; + + // Prevents page scroll while user is navigating + event.preventDefault(); + + let nextIndex = triggerIndex; + const homeIndex = 0; + const endIndex = triggerCount - 1; + + const moveNext = () => { + nextIndex = triggerIndex + 1; + if (nextIndex > endIndex) { + nextIndex = homeIndex; + } + }; + + const movePrev = () => { + nextIndex = triggerIndex - 1; + if (nextIndex < homeIndex) { + nextIndex = endIndex; + } + }; + + switch (event.key) { + case 'Home': + nextIndex = homeIndex; + break; + case 'End': + nextIndex = endIndex; + break; + case 'ArrowDown': + moveNext(); + break; + case 'ArrowUp': + movePrev(); + break; + } + + const clampedIndex = nextIndex % triggerCount; + triggerCollection[clampedIndex].ref.current?.focus(); + }; + + return ( + + + + ); + }, +); + +AccordionImpl.displayName = 'AccordionImpl'; + +/* ------------------------------------------------------------------------------------------------- + * AccordionItem + * -----------------------------------------------------------------------------------------------*/ + +type AccordionItemContextValue = { open?: boolean; triggerId: string }; +const [AccordionItemProvider, useAccordionItemContext] = + createContext('AccordionItem'); + +type AccordionItemElement = React.ComponentRef; +type CollapsibleProps = React.ComponentPropsWithoutRef; +interface AccordionItemProps + extends Omit { + /** + * A string value for the accordion item. All items within an accordion should use a unique value. + */ + value: string; +} + +/** + * `AccordionItem` contains all of the parts of a collapsible section inside of an `Accordion`. + */ +const AccordionItem = React.forwardRef( + (props, forwardedRef) => { + const { value, ...accordionItemProps } = props; + const valueContext = useAccordionValueContext(); + const triggerId = React.useId(); + const open = (value && valueContext.value.includes(value)) || false; + + return ( + + { + if (open) { + valueContext.onItemOpen(value); + } else { + valueContext.onItemClose(value); + } + }} + /> + + ); + }, +); + +AccordionItem.displayName = 'AccordionItem'; + +/* ------------------------------------------------------------------------------------------------- + * AccordionTrigger + * -----------------------------------------------------------------------------------------------*/ + +type AccordionTriggerElement = React.ComponentRef; +type CollapsibleTriggerProps = React.ComponentPropsWithoutRef; +interface AccordionTriggerProps extends CollapsibleTriggerProps {} + +/** + * `AccordionTrigger` is the trigger that toggles the collapsed state of an `AccordionItem`. It + * should always be nested inside of an `AccordionHeader`. + */ +const AccordionTrigger = React.forwardRef( + (props, forwardedRef) => { + const { ...triggerProps } = props; + const itemContext = useAccordionItemContext(); + + return ( + + + + ); + }, +); + +AccordionTrigger.displayName = 'AccordionTrigger'; + +/* ------------------------------------------------------------------------------------------------- + * AccordionContent + * -----------------------------------------------------------------------------------------------*/ + +type AccordionContentElement = React.ComponentRef; +type CollapsibleContentProps = React.ComponentPropsWithoutRef; +interface AccordionContentProps extends CollapsibleContentProps {} + +/** + * `AccordionContent` contains the collapsible content for an `AccordionItem`. + */ +const AccordionContent = React.forwardRef( + (props, forwardedRef) => { + const itemContext = useAccordionItemContext(); + + return ( + + ); + }, +); + +AccordionContent.displayName = 'AccordionContent'; + +/* -----------------------------------------------------------------------------------------------*/ + +export const Root = Accordion; +export const Item = AccordionItem; +export const Trigger = AccordionTrigger; +export const Content = AccordionContent; diff --git a/components/primitives/collapsible.tsx b/components/primitives/collapsible.tsx new file mode 100644 index 0000000..674de26 --- /dev/null +++ b/components/primitives/collapsible.tsx @@ -0,0 +1,134 @@ +'use client'; + +import * as React from 'react'; +import { createContext } from '@/components/utils/create-context'; +import { useControllableState } from '../utils/use-controllable-state'; +import { Primitive } from './primitive'; +import { useComposedRefs } from '../utils/compose-refs'; + +/* ------------------------------------------------------------------------------------------------- + * Collapsible + * -----------------------------------------------------------------------------------------------*/ + +type CollapsibleContextValue = { + contentId: string; + open: boolean; + onOpenToggle(): void; +}; + +const [CollapsibleProvider, useCollapsibleContext] = + createContext('Collapsible'); + +type CollapsibleElement = React.ComponentRef; +type PrimitiveDivProps = React.ComponentPropsWithoutRef; +interface CollapsibleProps extends PrimitiveDivProps { + defaultOpen?: boolean; + open?: boolean; + onOpenChange?(open: boolean): void; +} + +const Collapsible = React.forwardRef( + (props, forwardedRef) => { + const { open: openProp, defaultOpen, onOpenChange, ...collapsibleProps } = props; + + const [open = false, setOpen] = useControllableState({ + prop: openProp, + defaultProp: defaultOpen, + onChange: onOpenChange, + }); + + return ( + setOpen((prevOpen) => !prevOpen)} + > + + + ); + }, +); + +Collapsible.displayName = 'Collapsible'; + +/* ------------------------------------------------------------------------------------------------- + * CollapsibleTrigger + * -----------------------------------------------------------------------------------------------*/ + +type CollapsibleTriggerElement = React.ComponentRef; +type PrimitiveButtonProps = React.ComponentPropsWithoutRef; +interface CollapsibleTriggerProps extends PrimitiveButtonProps {} + +const CollapsibleTrigger = React.forwardRef( + (props, forwardedRef) => { + const { ...triggerProps } = props; + const context = useCollapsibleContext(); + return ( + + ); + }, +); + +CollapsibleTrigger.displayName = 'CollapsibleTrigger'; + +/* ------------------------------------------------------------------------------------------------- + * CollapsibleContent + * -----------------------------------------------------------------------------------------------*/ + +type CollapsibleContentElement = CollapsibleContentImplElement; +interface CollapsibleContentProps extends CollapsibleContentImplProps { + /** + * Used to force mounting when more control is needed. Useful when + * controlling animation with React animation libraries. + */ + forceMount?: true; +} + +const CollapsibleContent = React.forwardRef( + (props, forwardedRef) => { + const { forceMount, ...contentProps } = props; + const context = useCollapsibleContext(); + + return forceMount || context.open ? ( + + ) : null; + }, +); + +CollapsibleContent.displayName = 'CollapsibleContent'; + +/* -----------------------------------------------------------------------------------------------*/ + +type CollapsibleContentImplElement = React.ComponentRef; +interface CollapsibleContentImplProps extends PrimitiveDivProps {} + +const CollapsibleContentImpl = React.forwardRef< + CollapsibleContentImplElement, + CollapsibleContentImplProps +>((props, forwardedRef) => { + const { children, ...contentProps } = props; + const context = useCollapsibleContext(); + const ref = React.useRef(null); + const composedRefs = useComposedRefs(forwardedRef, ref); + + return ( + + {children} + + ); +}); + +CollapsibleContentImpl.displayName = 'CollapsibleImpl'; + +/* -----------------------------------------------------------------------------------------------*/ + +export const Root = Collapsible; +export const Trigger = CollapsibleTrigger; +export const Content = CollapsibleContent; diff --git a/components/primitives/collection.tsx b/components/primitives/collection.tsx new file mode 100644 index 0000000..cb3307a --- /dev/null +++ b/components/primitives/collection.tsx @@ -0,0 +1,118 @@ +'use client'; + +import React from 'react'; +import { createContext } from '@/components/utils/create-context'; +import { useComposedRefs } from '@/components/utils/compose-refs'; +import { Slot } from '@/components/primitives/slot'; + +type SlotProps = React.ComponentPropsWithoutRef; +type CollectionElement = HTMLElement; +interface CollectionProps extends SlotProps {} + +function createCollection(name: string) { + /* ----------------------------------------------------------------------------------------------- + * CollectionProvider + * ---------------------------------------------------------------------------------------------*/ + + type ContextValue = { + collectionRef: React.RefObject; + itemMap: Map< + React.RefObject, + { ref: React.RefObject } & ItemData + >; + }; + + const [CollectionProviderImpl, useCollectionContext] = createContext( + name + 'CollectionProvider', + { collectionRef: { current: null }, itemMap: new Map() }, + ); + + const CollectionProvider: React.FC<{ children?: React.ReactNode }> = (props) => { + const { children } = props; + const ref = React.useRef(null); + const itemMap = React.useRef(new Map()).current; + return ( + + {children} + + ); + }; + + CollectionProvider.displayName = name + 'CollectionProvider'; + + /* ----------------------------------------------------------------------------------------------- + * CollectionSlot + * ---------------------------------------------------------------------------------------------*/ + + const CollectionSlot = React.forwardRef( + (props, forwardedRef) => { + const { children } = props; + const context = useCollectionContext(); + const composedRefs = useComposedRefs(forwardedRef, context.collectionRef); + return {children}; + }, + ); + + CollectionSlot.displayName = name + 'CollectionSlot'; + + /* ----------------------------------------------------------------------------------------------- + * CollectionItem + * ---------------------------------------------------------------------------------------------*/ + + const ITEM_DATA_ATTR = 'data-collection-item'; + + type CollectionItemSlotProps = ItemData & { + children: React.ReactNode; + }; + + const CollectionItemSlot = React.forwardRef( + (props, forwardedRef) => { + const { children, ...itemData } = props; + const ref = React.useRef(null); + const composedRefs = useComposedRefs(forwardedRef, ref); + const context = useCollectionContext(); + + React.useEffect(() => { + context.itemMap.set(ref, { ref, ...(itemData as unknown as ItemData) }); + return () => void context.itemMap.delete(ref); + }); + + return ( + + {children} + + ); + }, + ); + + CollectionItemSlot.displayName = name + 'CollectionItemSlot'; + + /* ----------------------------------------------------------------------------------------------- + * useCollection + * ---------------------------------------------------------------------------------------------*/ + + function useCollection() { + const context = useCollectionContext(); + + const getItems = React.useCallback(() => { + const collectionNode = context.collectionRef.current; + if (!collectionNode) return []; + const orderedNodes = Array.from(collectionNode.querySelectorAll(`[${ITEM_DATA_ATTR}]`)); + const items = Array.from(context.itemMap.values()); + const orderedItems = items.sort( + (a, b) => orderedNodes.indexOf(a.ref.current!) - orderedNodes.indexOf(b.ref.current!), + ); + return orderedItems; + }, [context.collectionRef, context.itemMap]); + + return getItems; + } + + return [ + { Provider: CollectionProvider, Slot: CollectionSlot, ItemSlot: CollectionItemSlot }, + useCollection, + ] as const; +} + +export { createCollection }; +export type { CollectionProps }; diff --git a/components/primitives/hover-group.tsx b/components/primitives/hover-group.tsx new file mode 100644 index 0000000..0c5da7d --- /dev/null +++ b/components/primitives/hover-group.tsx @@ -0,0 +1,99 @@ +'use client'; + +import * as React from 'react'; +import { createContext } from '@/components/utils/create-context'; +import { MouseHover } from './mouse-hover'; +import { useCallbackRef } from '../utils/use-callback-ref'; +import { useControllableState } from '../utils/use-controllable-state'; + +/* ------------------------------------------------------------------------------------------------- + * HoverGroup + * -----------------------------------------------------------------------------------------------*/ + +type HoverGroupContextValue = { + value: string; + onValueChange(value: string): void; +}; + +const [HoverGroupProvider, useHoverGroup] = createContext('HoverGroup'); + +interface HoverGroupProps { + value?: string; + onValueChange?(value: string): void; + children: React.ReactNode; +} + +const HoverGroup = ({ + children, + value: valueProp, + onValueChange: onValueChangeProp, +}: HoverGroupProps) => { + const [value = '', setValue] = useControllableState({ + prop: valueProp, + onChange: onValueChangeProp, + }); + + return ( + + {children} + + ); +}; + +HoverGroup.displayName = 'HoverGroup'; + +/* ------------------------------------------------------------------------------------------------- + * HoverGroupItem + * -----------------------------------------------------------------------------------------------*/ + +type Mode = 'hovered' | 'dimmed' | 'initial'; + +type HoverGroupItemElement = React.ComponentRef; +interface HoverGroupItemProps { + value: string; + children: React.ReactNode; + onModeChange(mode: Mode): void; +} + +const HoverGroupItem = React.forwardRef( + ({ value, onModeChange, children }, forwardedRef) => { + const handleModeChange = useCallbackRef(onModeChange); + const context = useHoverGroup(); + const mode = getMode(context.value, value); + + React.useEffect(() => { + handleModeChange(mode); + }, [mode, handleModeChange]); + + return ( + { + context.onValueChange(hovered ? value : ''); + }} + asChild + ref={forwardedRef} + > + {children} + + ); + }, +); + +HoverGroupItem.displayName = 'HoverGroupItem'; + +/* -----------------------------------------------------------------------------------------------*/ + +function getMode(activeItem: string | null, item: string): Mode { + if (activeItem === item) { + return 'hovered'; + } + + if (activeItem && activeItem !== item) { + return 'dimmed'; + } + + return 'initial'; +} + +export const Root = HoverGroup; +export const Item = HoverGroupItem; diff --git a/components/primitives/mouse-hover.tsx b/components/primitives/mouse-hover.tsx new file mode 100644 index 0000000..9353efe --- /dev/null +++ b/components/primitives/mouse-hover.tsx @@ -0,0 +1,54 @@ +'use client'; + +import * as React from 'react'; +import { Primitive } from './primitive'; + +/* ------------------------------------------------------------------------------------------------- + * MouseHover + * -----------------------------------------------------------------------------------------------*/ + +type MouseHoverElement = React.ComponentRef; +type PrimitiveButtonProps = React.ComponentPropsWithoutRef; +interface MouseHoverProps extends PrimitiveButtonProps { + onValueChange: (active: boolean) => void; +} + +export const MouseHover = React.forwardRef( + ({ onValueChange, ...props }, forwardedRef) => { + const pointerTypeRef = React.useRef(undefined); + + return ( + { + props.onPointerDown?.(event); + pointerTypeRef.current = event.pointerType; + }} + onPointerEnter={(event) => { + props.onPointerEnter?.(event); + pointerTypeRef.current = event.pointerType; + }} + onMouseEnter={(event) => { + props.onMouseEnter?.(event); + if (isMousePointer(pointerTypeRef.current)) { + onValueChange(true); + } + }} + onMouseLeave={(event) => { + props.onMouseLeave?.(event); + onValueChange(false); + }} + ref={forwardedRef} + /> + ); + }, +); + +MouseHover.displayName = 'MouseHover'; + +/* -----------------------------------------------------------------------------------------------*/ + +function isMousePointer(pointerType: string | undefined) { + const values: Array = ['mouse', 'pen']; + return values.includes(pointerType); +} diff --git a/components/primitives/primitive.tsx b/components/primitives/primitive.tsx new file mode 100644 index 0000000..bcac423 --- /dev/null +++ b/components/primitives/primitive.tsx @@ -0,0 +1,46 @@ +import * as React from 'react'; +import { Slot } from '@/components/primitives/slot'; + +const NODES = [ + 'a', + 'button', + 'div', + 'form', + 'h2', + 'h3', + 'img', + 'input', + 'label', + 'li', + 'nav', + 'ol', + 'p', + 'span', + 'svg', + 'ul', +] as const; + +type Primitives = { [E in (typeof NODES)[number]]: PrimitiveForwardRefComponent }; +type PrimitivePropsWithRef = React.ComponentPropsWithRef & { + asChild?: boolean; +}; + +interface PrimitiveForwardRefComponent + extends React.ForwardRefExoticComponent> {} + +/* ------------------------------------------------------------------------------------------------- + * Primitive + * -----------------------------------------------------------------------------------------------*/ + +export const Primitive = NODES.reduce((primitive, node) => { + const Node = React.forwardRef((props: PrimitivePropsWithRef, forwardedRef: any) => { + const { asChild, ...primitiveProps } = props; + const Comp: any = asChild ? Slot : node; + + return ; + }); + + Node.displayName = `Primitive.${node}`; + + return { ...primitive, [node]: Node }; +}, {} as Primitives); diff --git a/components/primitives/scroll-area.tsx b/components/primitives/scroll-area.tsx new file mode 100644 index 0000000..393e762 --- /dev/null +++ b/components/primitives/scroll-area.tsx @@ -0,0 +1,486 @@ +'use client'; + +import * as React from 'react'; + +import { createContext } from '@/components/utils/create-context'; +import { useLayoutEffect } from '@/components/utils/use-layout-effect'; +import { useCallbackRef } from '@/components/utils/use-callback-ref'; +import { Slot } from '@/components/primitives/slot'; +import { useComposedRefs } from '@/components/utils/compose-refs'; + +type Sizes = { + content: number; + viewport: number; + scrollbar: { + size: number; + paddingStart: number; + paddingEnd: number; + }; +}; + +/* ------------------------------------------------------------------------------------------------- + * ScrollArea + * -----------------------------------------------------------------------------------------------*/ + +type ScrollAreaContextValue = { + scrollArea: ScrollAreaElement | null; + viewport: ScrollAreaViewportElement | null; + onViewportChange(viewport: ScrollAreaViewportElement | null): void; + content: HTMLDivElement | null; + onContentChange(content: HTMLDivElement): void; +}; + +const [ScrollAreaProvider, useScrollAreaContext] = + createContext('ScrollArea'); + +type ScrollAreaElement = React.ComponentRef<'div'>; +interface ScrollAreaProps extends React.ComponentPropsWithoutRef<'div'> { + asChild?: boolean; +} + +const ScrollArea = React.forwardRef( + ({ asChild, ...props }, forwardedRef) => { + const [scrollArea, setScrollArea] = React.useState(null); + const [viewport, setViewport] = React.useState(null); + const [content, setContent] = React.useState(null); + const composedRefs = useComposedRefs((node) => setScrollArea(node), forwardedRef); + + const Component = asChild ? Slot : 'div'; + + return ( + + + + ); + }, +); + +ScrollArea.displayName = 'ScrollArea'; + +/* ------------------------------------------------------------------------------------------------- + * ScrollAreaViewport + * -----------------------------------------------------------------------------------------------*/ + +type ScrollAreaViewportElement = React.ComponentRef<'div'>; +interface ScrollAreaViewportProps extends React.ComponentPropsWithoutRef<'div'> {} + +const ScrollAreaViewport = React.forwardRef( + (props, forwardedRef) => { + const { children, ...viewportProps } = props; + const context = useScrollAreaContext(); + const ref = React.useRef(null); + const composedRefs = useComposedRefs(forwardedRef, ref, (node) => + context.onViewportChange(node), + ); + + return ( + <> + {/* Hide scrollbars cross-browser and enable momentum scroll for touch devices */} +