I've been building and selling Next.js templates as a side project. This is my fifth one: Craft, a developer portfolio template with a typography-first approach.
Here are all the decisions I made.
Why typography-first?
Most dev portfolio templates use gradients, animations, and card grids to grab attention. I wanted to try the opposite — let the type do the work, and keep everything else minimal.
Font pairing: Lora (serif) for headings, JetBrains Mono for labels and code, Inter for body text. Three families that complement each other without fighting.
The serif gives headings an editorial quality that stands out from the usual Inter/Geist sans-serif stack.
The blinking cursor
The hero has an animated blinking cursor after the tech stack line:
function Cursor() {
const [visible, setVisible] = useState(true)
useEffect(() => {
const id = setInterval(() => setVisible(v => !v), 530)
return () => clearInterval(id)
}, [])
return (
_
)
}
530ms interval feels natural — not too fast, not too slow. Hidden from screen readers via aria-hidden.
The bento grid for projects
Same "1px gap trick" I use in all my templates: grid background = border color, gap = 1px, no borders on individual cards.
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1px;
background: var(--border-sub); /* this IS the border */
border-radius: var(--r-lg);
overflow: hidden;
}
/* First project gets more visual weight */
.wide { grid-column: span 2; }
The experience timeline
Pure CSS — no JavaScript, no library:
.timeline {
position: relative;
}
/* The vertical line */
.timeline::before {
content: '';
position: absolute;
left: 56px;
top: 6px; bottom: 6px;
width: 1px;
background: var(--border);
}
.item {
display: grid;
grid-template-columns: 56px 16px 1fr;
gap: 0 20px;
}
The three-column grid: year column (fixed width) → dot column (fixed width) → content. The ::before pseudo-element on the list creates the connector line behind all dots.
All content in one file
src/lib/constants.ts exports everything the buyer will want to edit:
- Personal info (name, role, tagline, bio, email)
- Social links
- Stack (with
highlight: truefor primary skills) - Projects (with live URL, repo URL, metrics)
- Experience
- Services
- SEO metadata
No need to touch any component file. Change constants.ts and the entire site updates.
Real favicon included
This is the thing most templates skip. I include:
-
public/favicon.ico— browser tab icon -
public/favicon.svg— modern SVG version
And in layout.tsx:
Both are required. Some browsers prefer .ico, modern browsers prefer .svg.
Live demo: https://craft-portfolio-rose.vercel.app
Available on Gumroad for $39: https://devmaya.gumroad.com/l/craft
Top comments (0)