From 52fd62cc74d561b5a6cf3188f3712fe5e94852cd Mon Sep 17 00:00:00 2001 From: Himank Dave Date: Sun, 13 Apr 2025 18:02:32 -0400 Subject: [PATCH 1/6] style: decrease padding around highlighted text by Highlighter component --- src/components/ui/Highlighter.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ui/Highlighter.tsx b/src/components/ui/Highlighter.tsx index af02f99..35db817 100644 --- a/src/components/ui/Highlighter.tsx +++ b/src/components/ui/Highlighter.tsx @@ -37,5 +37,5 @@ export default function Highlight({ text, color = 'yellow' }: HighlightProps) { colorClass = 'bg-[#ffff77] dark:bg-[#fce913]/30'; } - return {text}; + return {text}; } From 2c9824da73e76aff3d8015cf39382906cd36711e Mon Sep 17 00:00:00 2001 From: Himank Dave Date: Sun, 13 Apr 2025 18:44:52 -0400 Subject: [PATCH 2/6] refactor(highlight): simplify color logic using color map --- src/components/ui/Highlighter.tsx | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/components/ui/Highlighter.tsx b/src/components/ui/Highlighter.tsx index 35db817..ed0a6cd 100644 --- a/src/components/ui/Highlighter.tsx +++ b/src/components/ui/Highlighter.tsx @@ -22,20 +22,21 @@ interface HighlightProps { * */ export default function Highlight({ text, color = 'yellow' }: HighlightProps) { - let colorClass: string; + const colors: Record = { + cyan: 'bg-cyan-200 dark:bg-cyan-500/30', + pink: 'bg-[#ffa7ee] dark:bg-[#f73ed2]/30', + 'slate-blue': 'bg-[#a0a8ff] dark:bg-[#675bf9]/50', + red: 'bg-[#ffa0a0] dark:bg-[#f83b3b]/50', + yellow: 'bg-[#ffff77] dark:bg-[#fce913]/30', + }; - if (color === 'cyan') { - colorClass = 'bg-cyan-200 dark:bg-cyan-500/30'; - } else if (color === 'pink' || color === 'violet-web') { - colorClass = 'bg-[#ffa7ee] dark:bg-[#f73ed2]/30'; - } else if (color === 'slate-blue') { - colorClass = 'bg-[#a0a8ff] dark:bg-[#675bf9]/50'; - } else if (color === 'red') { - colorClass = 'bg-[#ffa0a0] dark:bg-[#f83b3b]/50'; - } else { - // This covers "yellow", "lemon", "laser-lemon", and the default case - colorClass = 'bg-[#ffff77] dark:bg-[#fce913]/30'; + if (color === 'violet-web') { + color = 'pink'; + } else if (color === 'lemon' || color === 'laser-lemon') { + color = 'yellow'; } - return {text}; + const colorClass = colors[color]; + + return {text}; } From 776f66ddebc86a80bb6e2976888a7624ca9dcefa Mon Sep 17 00:00:00 2001 From: Himank Dave Date: Sun, 13 Apr 2025 19:26:01 -0400 Subject: [PATCH 3/6] feat: add StrikeHighlight component for colorful strikethrough-style text highlights --- src/components/ui/StrikeHighlighter.tsx | 52 +++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/components/ui/StrikeHighlighter.tsx diff --git a/src/components/ui/StrikeHighlighter.tsx b/src/components/ui/StrikeHighlighter.tsx new file mode 100644 index 0000000..fbf35ed --- /dev/null +++ b/src/components/ui/StrikeHighlighter.tsx @@ -0,0 +1,52 @@ +import { cn } from '@/lib/utils'; + +interface StrikeHighlightProps { + text: string; + color?: + | 'cyan' + | 'pink' + | 'violet-web' + | 'slate-blue' + | 'red' + | 'yellow' + | 'lemon' + | 'laser-lemon'; +} + +/** + * Highlights the given text with the highlighter of specified color with a strikethrough effect. + * + * @param text - the text to be highlighted + * @param color - the color of the highlighter [cyan,pink,violet-web,slate-blue,red,yellow,lemon,laser-lemon] + * @returns Returns a `span` element with the highlighted text of specified color with strikethrough effect. + * + */ +export default function StrikeHighlight({ text, color = 'yellow' }: StrikeHighlightProps) { + const colors: Record = { + cyan: 'bg-cyan-200 dark:bg-cyan-500/45', + pink: 'bg-[#ffa7ee] dark:bg-[#f73ed2]/50', + 'slate-blue': 'bg-[#a0a8ff] dark:bg-[#675bf9]/50', + red: 'bg-[#ffa0a0] dark:bg-[#f83b3b]/45', + yellow: 'bg-[#ffff77] dark:bg-[#fce913]/35', + }; + + if (color === 'violet-web') { + color = 'pink'; + } else if (color === 'lemon' || color === 'laser-lemon') { + color = 'yellow'; + } + + const colorClass = colors[color]; + + return ( + + + {text} + + ); +} From 521349f82d9ed52df7fb3653740d3e6bcc91fc2c Mon Sep 17 00:00:00 2001 From: Himank Dave Date: Sun, 13 Apr 2025 20:30:00 -0400 Subject: [PATCH 4/6] feat: add and customize Accordion component from shadcn --- package-lock.json | 258 +++++++++++++++++++++++++++++++- package.json | 1 + src/components/ui/Accordion.tsx | 54 +++++++ tailwind.config.ts | 14 ++ 4 files changed, 326 insertions(+), 1 deletion(-) create mode 100644 src/components/ui/Accordion.tsx diff --git a/package-lock.json b/package-lock.json index 64540bb..4beeb54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@mdx-js/loader": "^3.1.0", "@mdx-js/react": "^3.1.0", "@next/mdx": "^15.1.6", + "@radix-ui/react-accordion": "^1.2.4", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "copy-to-clipboard": "^3.3.3", @@ -1029,6 +1030,261 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", + "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==" + }, + "node_modules/@radix-ui/react-accordion": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.4.tgz", + "integrity": "sha512-SGCxlSBaMvEzDROzyZjsVNzu9XY5E28B3k8jOENyrz6csOv/pG1eHyYfLJai1n9tRjwG61coXDhfpgtxKxUv5g==", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-collapsible": "1.1.4", + "@radix-ui/react-collection": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.0.3", + "@radix-ui/react-use-controllable-state": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.4.tgz", + "integrity": "sha512-u7LCw1EYInQtBNLGjm9nZ89S/4GcvX1UR5XbekEgnQae2Hkpq39ycJ1OhdeN1/JDfVNG91kWaWoest127TaEKQ==", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.3", + "@radix-ui/react-primitive": "2.0.3", + "@radix-ui/react-use-controllable-state": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.3.tgz", + "integrity": "sha512-mM2pxoQw5HJ49rkzwOs7Y6J4oYH22wS8BfK2/bBxROlI4xuR0c4jEenQP63LlTlDkO6Buj2Vt+QYAYcOgqtrXA==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.0.3", + "@radix-ui/react-slot": "1.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.3.tgz", + "integrity": "sha512-IrVLIhskYhH3nLvtcBLQFZr61tBG7wx7O3kEmdzcYwRGAEBmBicGGL7ATzNgruYJ3xBTbuzEEq9OXJM3PAX3tA==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.3.tgz", + "integrity": "sha512-Pf/t/GkndH7CQ8wE2hbkXA+WyZ83fhQQn5DDmwDiDo6AwN/fhaH8oqZ0jRjMrO2iaMhDi6P1HRx6AZwyMinY1g==", + "dependencies": { + "@radix-ui/react-slot": "1.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", + "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.1.tgz", + "integrity": "sha512-YnEXIy8/ga01Y1PN0VfaNH//MhA91JlEGVBDxDzROqwrAtG5Yr2QGEPz8A/rJA3C7ZAHryOYGaUv8fLSW2H/mg==", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@remix-run/router": { "version": "1.23.0", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", @@ -1158,7 +1414,7 @@ "version": "18.3.6", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.6.tgz", "integrity": "sha512-nf22//wEbKXusP6E9pfOCDwFdHAX4u172eaJI4YkDRQEZiorm6KfYnSC2SWLDMVWUOWPERmJnN0ujeAfTBLvrw==", - "dev": true, + "devOptional": true, "peerDependencies": { "@types/react": "^18.0.0" } diff --git a/package.json b/package.json index b330c6f..7b30841 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@mdx-js/loader": "^3.1.0", "@mdx-js/react": "^3.1.0", "@next/mdx": "^15.1.6", + "@radix-ui/react-accordion": "^1.2.4", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "copy-to-clipboard": "^3.3.3", diff --git a/src/components/ui/Accordion.tsx b/src/components/ui/Accordion.tsx new file mode 100644 index 0000000..52ca804 --- /dev/null +++ b/src/components/ui/Accordion.tsx @@ -0,0 +1,54 @@ +'use client'; + +import * as AccordionPrimitive from '@radix-ui/react-accordion'; +import * as React from 'react'; + +import { cn } from '@/lib/utils'; + +const Accordion = AccordionPrimitive.Root; + +const AccordionItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AccordionItem.displayName = 'AccordionItem'; + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180", + className, + )} + {...props} + > + {children} + {/* */} + + +)); +AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName; + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)); + +AccordionContent.displayName = AccordionPrimitive.Content.displayName; + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }; diff --git a/tailwind.config.ts b/tailwind.config.ts index 4385a4e..fa13336 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -160,6 +160,20 @@ const config: Config = { '950': '#013219', }, }, + keyframes: { + 'accordion-down': { + from: { height: '0' }, + to: { height: 'var(--radix-accordion-content-height)' }, + }, + 'accordion-up': { + from: { height: 'var(--radix-accordion-content-height)' }, + to: { height: '0' }, + }, + }, + animation: { + 'accordion-down': 'accordion-down 0.2s ease-out', + 'accordion-up': 'accordion-up 0.2s ease-out', + }, }, }, plugins: [], From 91839744df83ba36d94c2d7e34c5d99d6e182148 Mon Sep 17 00:00:00 2001 From: Himank Dave Date: Sun, 13 Apr 2025 22:03:56 -0400 Subject: [PATCH 5/6] feat: convert extra info paragraphs to AccordionForExtraInfo component in About component Refactored the static informational paragraphs into a dynamic Accordion component. --- src/components/About.tsx | 192 ++++++++++++++++++++++++++------------- 1 file changed, 131 insertions(+), 61 deletions(-) diff --git a/src/components/About.tsx b/src/components/About.tsx index 4843a96..0b33ba9 100644 --- a/src/components/About.tsx +++ b/src/components/About.tsx @@ -1,9 +1,132 @@ +import { FlagTriangleRight, GitCommitVertical, Signpost } from 'lucide-react'; + +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '@/components/ui/Accordion'; import BlurFade, { BLUR_FADE_DELAY } from '@/components/ui/BlurFade'; -import Highlight from '@/components/ui/Highlighter'; import LinkWithArrow from '@/components/ui/LinkWithArrow'; +import StrikeHighlight from '@/components/ui/StrikeHighlighter'; import { cn } from '@/lib/utils'; +type CustomAccordionItemDetails = { + value: string; + iconTitle: React.ReactNode; + title: string; + iconBody: React.ReactNode; + body: React.ReactNode[]; +}; + +const AccordionForExtraInfo = (items: CustomAccordionItemDetails[] = []) => { + const AccordionItemCustom = ({ + value, + iconTitle, + title, + iconBody, + body, + }: CustomAccordionItemDetails) => { + return ( + + + + {iconTitle} + {title} + + + +
    + {body.map((node, idx) => ( +
  • + {iconBody} + {node} +
  • + ))} +
+
+
+ ); + }; + + return ( + + {items.map((item, idx) => ( + + ))} + + ); +}; + const About = () => { + const findMeDoingBody = [ + <> + any one of these series:{' '} + ,{' '} + ,{' '} + and{' '} + . + , + ]; + const thoseOfYouCuriousBody = [ + <> + The favicon is a made via Python, + using NetworkX and Matplotlib. Check the{' '} + + image + + out, learn{' '} + + more + {' '} + about hypercube graphs & here is the{' '} + + code + {' '} + to generate it yourself. + , + ]; + const itemsForExtraInfo: CustomAccordionItemDetails[] = [ + { + value: 'findme', + iconTitle: ( + <> +