frontend · starter · 4d
Personal portfolio page
Build a one-page site about you with plain HTML, CSS, and a sprinkle of JavaScript — no framework, no build step, just files you can open in a browser. By the end you'll have something real to show, and you'll actually understand every line of it because you wrote it yourself.
Deliverable
A responsive single-page portfolio (hero, about, projects, contact) that looks good on phone and laptop and has one small piece of interactivity you wired up with JavaScript — opened straight from an index.html file.
Milestones
0/5 · 0%- 01Structure it in HTML
Start with the skeleton: a single index.html with clear, meaningful sections — a header with your name, an 'about' paragraph, a list of two or three projects, and a contact link. Use real semantic tags (header, main, section, nav, footer) instead of a pile of divs. No styling yet: a plain black-on-white page that already reads top to bottom and makes sense is the win here. Open the file directly in your browser to see it.
Definition of done- An index.html opens in the browser and shows your name, an about paragraph, a project list, and a contact link.
- The page uses semantic elements (header/main/section/footer), not just nested divs.
Self-review
Show index.html in browser with name/about/projects/contact and semantic tags (header/main/section). A reviewer checks no styling yet but structure reads top-to-bottom.
- 02Style it with CSS
Link a stylesheet and make the page yours: pick a font, a couple of colors, comfortable spacing, and lay the sections out with flexbox so things line up instead of stacking awkwardly. Aim for calm and readable, not flashy. The skill you're building is seeing a page as boxes you can size, space, and align — once that clicks, every layout gets easier.
Definition of done- A linked CSS file sets typography, colors, and spacing, and at least one section is laid out with flexbox.
- The page looks intentional and readable, not like an unstyled default document.
Self-review
Show linked CSS with flexbox layout and intentional typography/colors. A reviewer checks at least one flexbox section and readable spacing.
- 03Make it work on a phone
Most people will see your page on a phone, so it has to hold up at narrow widths. Add the viewport meta tag, then use a media query to adjust layout for small screens — stack columns, shrink the hero, bump up the spacing. Resize the browser window and watch it adapt. Responsive isn't a separate page; it's the same HTML choosing to lay out differently when there's less room.
Definition of done- The page has a viewport meta tag and at least one media query that changes the layout on narrow screens.
- At a phone-sized width, nothing overflows horizontally and the content stays readable.
Self-review
Resize to 320px and show viewport meta + media query with no horizontal overflow. A reviewer checks no overflow at phone width.
- 04Bring it to life with JavaScript
Add one small interactive touch with vanilla JavaScript — a dark-mode toggle, a 'copy email' button, or a mobile menu that opens and closes. Select the element, listen for a click, and change something on the page in response. Keep it to a single feature done well. This is the core loop of all frontend interactivity: find an element, react to an event, update the DOM. Get it once and the rest is just more of the same.
Definition of done- Clicking your control visibly changes the page (theme, copied state, or open/closed menu).
- The JavaScript selects elements and attaches a click handler — no inline onclick in the HTML.
Self-review
Click the control and show visible change (theme/copy/menu) with JS handler outside HTML. A reviewer checks querySelector + addEventListener, no inline onclick.
- 05Deploy and measure
Deploy your page for free (GitHub Pages, Netlify, or Cloudflare Pages) so it has a real URL, then measure it. Run Lighthouse (mobile) and record LCP, CLS, and the audit for accessibility: images have explicit width/height (no CLS), fonts use font-display: swap, the interactive control is keyboard-operable with visible focus and aria-expanded where appropriate. Fix any red: a missing alt, a contrast <4.5:1, or a layout shift from an unsized hero image. The point is to close the loop from 'file on disk' to 'URL a stranger can open' and to see your CWV numbers go from red to green because you set the basics right.
Definition of done- The page is deployed to a public URL and Lighthouse mobile shows LCP < 2.5s, CLS < 0.1, and no critical a11y violations.
- Images have explicit dimensions, fonts use swap, and the interactive control is keyboard-operable with visible focus.
Self-review
Show the public URL and Lighthouse report (LCP/CLS/a11y). A reviewer checks the CWV numbers are green and the a11y control has focus + ARIA.
Starter
fallowlone/skein-projects
projects/personal-portfolio-page
- README.md
- artifact/index.html
- artifact/main.js
- artifact/style.css
- src/page.ts
- test/page.test.ts
npx degit fallowlone/skein-projects/projects/personal-portfolio-page personal-portfolio-page Implement the stubs, then run the tests until they pass: bun test
Fork the repo and push your work — the grade workflow runs the suite plus static checks on your own runners.
Rubric
| Junior | Mid | Senior | |
|---|---|---|---|
| Semantic HTML & accessibility | Sections exist and the page reads top-to-bottom; generic div wrappers are used where semantic elements would be more appropriate. | header / main / section / nav / footer are used correctly; images have alt text, links are keyboard-navigable, and contrast meets WCAG AA (≥4.5:1 for body text). | The interactive element (toggle / menu) is keyboard-operable with visible focus, the correct ARIA role and aria-expanded / aria-controls attributes, and the page passes an axe-core or Lighthouse accessibility audit with no critical violations. |
| Responsive layout without overflow | A viewport meta tag is present; the page is narrowable but some elements overflow or text becomes unreadably small on phone widths. | At least one media query adjusts layout for small screens; nothing overflows horizontally at phone width (320 px) and all content stays readable. | Layout uses intrinsic sizing (min-content / max-content / clamp) or fluid units so breakpoints are the exception rather than the rule; the page is tested at 320, 768, and 1440 px with zero overflow and correct tap-target size (≥44 px) for interactive elements. |
| Performance budget & CWV | The page loads but images have no explicit dimensions (CLS risk), fonts block rendering, and no Lighthouse or PageSpeed data has been collected. | Images have explicit width/height (no CLS), fonts use font-display: swap, and a Lighthouse run shows LCP < 2.5 s and CLS < 0.1 on a mobile simulation. | The hero image is preloaded with fetchpriority='high', below-fold images use loading='lazy', the page is deployed and a real PageSpeed / CrUX run confirms the CWV targets (LCP < 2.5 s, CLS < 0.1, no layout shift from font swap). You can name which resource set each CWV and what would break the budget. |
Reference walkthrough (spoiler)
Semantic HTML value: semantic elements give the browser (and assistive technology) a structural contract for free — a nav is implicitly a landmark region, a button is keyboard-focusable and fires on Enter/Space without JS, a header implies banner role. Replacing them with divs+roles works but requires re-implementing all of that behaviour manually and getting it wrong is easy.
CLS root causes on a portfolio page: the three most common layout-shift sources are images without explicit dimensions (the browser doesn't reserve space until the image loads), web fonts (text reflows when the custom font swaps in), and late-injected content (a cookie banner or analytics widget pushing the hero down). Fixing all three costs nothing in code size and moves LCP and CLS from red to green.
Responsive without breakpoints: most overflow issues on narrow screens come from fixed pixel widths. Replacing width: 400px with max-width: 100% or using clamp() for font sizes removes the need for many breakpoints. min-width: 0 on flex children prevents a child with long text from overflowing its flex container — one of the most common bugs on first responsive builds.
Make it senior
- Deploy it for free (GitHub Pages, Netlify, or Cloudflare Pages) so it has a real URL you can put on your resume.
- Run a quick accessibility and Lighthouse pass: check colour contrast, add alt text to images, and make sure the page is fully usable with the keyboard.