Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

UI/UX Design  Module 15 – Portfolio and career

UX portfolio case studies

Choose two to four projects, lead each case study with the problem, your role and the result, share evidence with consent and publish an accessible page.

  • Intermediate
  • 20 minutes
  • Examples run with Node.js 24.21.0, Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free lesson, Pro answers: 20 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Choose two to four projects that show your process, judgement and outcomes
  • Write a case study in seven parts, from the problem to a reflection
  • Show confidential, team and self-initiated work honestly, crediting others and any AI help
  • Publish a case-study page that meets WCAG 2.2 AA and loads quickly on a phone

Before you start

What you’ll learn · Before you start

What you will learn

  • Choose two to four projects that show your process, judgement and outcomes
  • Write a case study in seven parts, from the problem to a reflection
  • Show confidential, team and self-initiated work honestly, crediting others and any AI help
  • Publish a case-study page that meets WCAG 2.2 AA and loads quickly on a phone

Before you start

On this page

A case study of 2,400 words and 12 images takes 11 minutes 26 seconds to read at 238 words a minute, the average speed of adults reading English non-fiction silently, and the person deciding whether to invite you may stop after the first screen. A UX portfolio: A small set of case studies that shows how a designer works: the problems, the decisions and their reasons, and what changed as a result. works when each case study is written for that reader: the problem, your role and the result first, then the decisions only you can explain.

You will choose the projects worth showing, write each case study: A written account of one design project for a reader who was not there: the problem, your role, the constraints, the evidence, the decisions, the outcome and a reflection. in seven parts, share research evidence you have permission to share, and publish a page that is itself a sample of accessible, fast design. Two programs check a draft the way a careful reader’s tools would.

Readers skim, so the first screen carries the problem, your role and the result

Whoever opens your portfolio, a recruiter, a design lead or a panel, reads the first screen of each case study to decide whether the rest is worth their time. On a phone that screen holds a short paragraph or two and perhaps one image. Put three things there: the problem and who had it, what you did, and what changed. The research, the sketches and the iterations support those three and come later.

The two openings describe the same MealBox project; MealBox, Ward Desk and every number about them are invented for this track. Count what you know after the first two sentences of each:

Opening What a reader knows after two sentences
My process followed the double diamond. I started with desk research on the meal-kit market, then created personas and a journey map. The method, but not the problem, your part or any result
MealBox lost 31 % of new subscribers in their first week, most before the first box arrived. As the only designer in a team of five, I led six interviews and redesigned the order confirmation; in the subscription records, first-week cancellations fell to 22 % over the next two months. The problem, who had it, your role, what changed and where the number comes from

The strong version is barely longer, yet it answers the reader’s three questions in the order they ask them. After the first screen, show the decisions and trade-offs only you can explain, and leave out artefacts that changed nothing, however much work they took.

Opening with your process

Wrong: a first screen that lists the stages of your process, or shows a persona before the problem. Right: the problem, your role and the result first. The process is how you solved the problem, so it belongs in the middle, where a reader who is already interested goes looking for it.

Checkpoint

Check: The first screen of a case study

  1. Question 1 of 2 Which opening sentence gives a reader the most in the first screen of a case study?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Ward Desk lost a third of streetlight reports to missing locations; I redesigned the form, and unusable reports fell from 14 in 40 to 3 in 40.

    The first screen carries three things: the problem and who had it, your role, and what changed. The process comes later, for the reader who is already interested.

  2. Question 2 of 2 You made 14 artefacts on a project, and four of them changed a decision. What do you show?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: The four, each next to the decision it changed

    Show the decisions and trade-offs only you can explain, and the artefacts that changed them. Leave out work that changed nothing, however long it took.

Two to four projects, chosen for the judgement they show

Each case study asks for a reader’s time, so two strong ones serve you better than six thin ones. Choose projects that together show process, judgement and outcomes, and that differ from each other: one weighted towards research, one towards interaction or visual design, one where a constraint forced a hard trade-off. Prefer recent work, and work you could present for 15 minutes and defend under questions, which the next lesson, Design interviews and design challenges, prepares you for.

Not every good project belongs. Answer the three questions about one project you are weighing; the table marks the kinds of project that still fit, with the reason:

Interactive · answer and compare Which projects belong in your portfolio? Answer three questions about a project you are weighing; the table marks the kinds that still fit and why.
Did real people use the work, outside a classroom or a team demo?
Can you name at least two decisions that were yours, with the reason for each?
Can you show the work in public, or get permission to?

Answer the questions above: the table then marks what fits your case and says why the rest does not.

Six kinds of project against the evidence a case study needs and whether you can show it in public.
Kind of project Shows your process Shows your decisions Has a measured outcome Can be shown in public
Shipped work in a team Typical evidence: research, releases, live measures Good research, iterations and releases you can describe step by step Mixed strong if you can say which decisions were yours Good live measures before and after, when the team shares them Mixed often confidential: ask permission or describe it in general terms
Shipped work on your own Typical evidence: a client or a small product Good every step is yours to show Good every decision was yours, with its reason Mixed measures exist if the client or the product tracked them Mixed ask the client before you publish their screens
A course project Typical evidence: a brief and a test with peers Good a full process, often with every stage written up Mixed the brief may have made the big decisions for you Poor rarely more than a test with classmates Good usually yours to show; label it as a course project
A volunteer project Typical evidence: real users, small scale Good real constraints: little time, little money, real people Good trade-offs you had to make yourself Mixed small numbers, but real ones, from real users Good usually shareable once the organisation agrees
A self-initiated redesign Typical evidence: public information only Mixed research is limited to what is public or what you test yourself Good every decision is yours Poor no live measure; a test with five people at most Mixed yes, if labelled as unaffiliated and built on public information
A hackathon prototype Typical evidence: a demo after two days Poor two days leave little room for research or iteration Mixed quick decisions, rarely tested Poor a demo, not a measure Good usually shareable, with the team credited
How the answers decide
  • “Did real people use the work, outside a classroom or a team demo?” No, only testers or classmates: adds a caveat to A course project, A self-initiated redesign and A hackathon prototype. Without real users the outcome is a test at best, so say what you tested and with whom, and pair it with one project that has real users.
  • “Can you name at least two decisions that were yours, with the reason for each?” Not clearly: adds a caveat to Shipped work in a team, Shipped work on your own, A course project, A volunteer project, A self-initiated redesign and A hackathon prototype. A case study is a record of your decisions. Work out two that were yours, and why, apart from any team's; if you cannot, leave the project out.
  • “Can you show the work in public, or get permission to?” No, it is confidential: adds a caveat to Shipped work in a team, Shipped work on your own, A course project, A volunteer project, A self-initiated redesign and A hackathon prototype. Ask for permission first. If it is refused, describe the problem and your process in general terms, with figures as ranges and no screens.

Course projects and a self-initiated project: Design work done on your own initiative, with no client or employer behind it, such as a redesign of a public product; shown in a portfolio with that label. are welcome as long as they are labelled. Pair them with at least one project that had real users, so that a reader also sees how you work when the people and the constraints are real.

Checkpoint

Check: Choosing two to four projects

  1. Question 1 of 2 How many case studies does the lesson advise a UX portfolio to carry?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Two to four

    Two to four projects that differ from each other show range without asking more of the reader than they will give.

  2. Question 2 of 2 Your three candidates are two course projects and one self-initiated redesign. What does the lesson suggest you add or change?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Add a project that had real users, even a small volunteer one, and label the others for what they are

    Label every project for what it is, and pair projects without users with at least one that had them.

Seven parts, each answering a question the reader has

A case study answers seven questions, roughly in this order. Each part can be short, but none can be missing, because a reader who meets a gap fills it with a guess:

Part The reader’s question
Problem What was wrong, for whom, and why did it matter?
Role What did you do, and with whom?
Constraints What limited the work: time, platform, data or policy?
Evidence How did you know, and from whom?
Decisions and trade-offs What did you choose, over what, and what did it cost?
Outcome What changed, and how was it measured?
Reflection What would you do differently, and what comes next?

The program checks a draft of the MealBox case study for those parts as h2 headings, for headings that skip a level, which the W3C headings tutorial says can confuse readers and should be avoided, and for its reading time by the rules of MySmartCoPilot’s Reading Time Calculator:

A case-study draft checked for parts, headings and length Python · case_study_check/case_study_check.py
"""Check a case-study draft before it goes on a portfolio: the seven parts, the heading order and the reading time.

The draft is an invented MealBox project. Reading time follows MySmartCoPilot's Reading Time Calculator: 238 words a
minute, the average for silent reading of English non-fiction (Brysbaert, 2019), plus 12 seconds for the first image,
one second less for each next one and never under 3 seconds.
"""

from html.parser import HTMLParser

WPM = 238
LIMIT_MINUTES = 8
PARTS = {  # part: words that mark its heading
    "problem": ["problem"],
    "role": ["role"],
    "constraints": ["constraint", "limit"],
    "evidence": ["research", "evidence"],
    "decisions": ["decision", "trade-off"],
    "outcome": ["outcome", "result"],
    "reflection": ["reflection", "next time", "differently"],
}

DRAFT = """
<h1>Fewer first-week cancellations at MealBox</h1>
<p>MealBox lost 31 % of new subscribers in their first week. I was the only designer on a team of five.</p>
<h2>My role</h2>
<p>I planned and ran six interviews, mapped the first week with the support team and designed the new flow.</p>
<h2>The problem</h2>
<p>New subscribers did not know when the first box would arrive, so many cancelled before it came.</p>
<h4>What we measured</h4>
<p>Cancellations in the first seven days, from the subscription records, for every new subscriber.</p>
<h2>Research evidence</h2>
<p>Five of six people we interviewed checked their email for a delivery day that the old flow never sent.</p>
<img src="affinity-map.png" alt="Notes grouped into four themes; the largest is not knowing the delivery day">
<h2>Decisions and trade-offs</h2>
<p>We showed the delivery day on the confirmation screen and dropped a welcome video to keep the page light.</p>
<img src="flow-v2.png" alt="The new confirmation screen with the delivery day at the top">
<h2>Outcome</h2>
<p>First-week cancellations fell from 31 % to 22 % in the two months after launch, from the same records.</p>
"""


class Outline(HTMLParser):
    def __init__(self):
        super().__init__()
        self.headings, self.words, self.images, self._level = [], 0, 0, None

    def handle_starttag(self, tag, attrs):
        if tag in ("h1", "h2", "h3", "h4", "h5", "h6"):
            self._level = int(tag[1])
            self.headings.append([self._level, ""])
        elif tag == "img":
            self.images += 1

    def handle_endtag(self, tag):
        if tag.startswith("h") and tag[1:].isdigit():
            self._level = None

    def handle_data(self, data):
        self.words += len(data.split())
        if self._level is not None:
            self.headings[-1][1] += data


def image_seconds(count):
    return sum(max(3, 12 - i) for i in range(count))


def reading_seconds(words, images):
    return words / WPM * 60 + image_seconds(images)


def mm_ss(seconds):
    whole = round(seconds)
    return f"{whole // 60} min {whole % 60} s"


page = Outline()
page.feed(DRAFT)

found = []
for level, text in page.headings:
    for part, marks in PARTS.items():
        if level == 2 and any(m in text.lower() for m in marks) and part not in found:
            found.append(part)
print("Parts found, in page order:", ", ".join(found))
print("Missing:", ", ".join(p for p in PARTS if p not in found) or "none")

previous = 1
for level, text in page.headings[1:]:
    if level > previous + 1:
        print(f"Heading order: '{text}' is h{level} straight after an h{previous}")
    previous = level

seconds = reading_seconds(page.words, page.images)
print(f"Words {page.words}, images {page.images}: {mm_ss(seconds)} to read (limit {LIMIT_MINUTES} min)")
print()
print("Words that fit in 8 minutes, by number of images:")
for images in (0, 6, 12):
    room = LIMIT_MINUTES * 60 - image_seconds(images)
    print(f"  {images:>2} images: {image_seconds(images):>3} s for images, {room / 60 * WPM:,.0f} words")
long_read = reading_seconds(2400, 12)
print(f"A 2,400-word study with 12 images takes {mm_ss(long_read)}.")

Output

Parts found, in page order: role, problem, evidence, decisions, outcome
Missing: constraints, reflection
Heading order: 'What we measured' is h4 straight after an h2
Words 146, images 2: 1 min 0 s to read (limit 8 min)

Words that fit in 8 minutes, by number of images:
   0 images:   0 s for images, 1,904 words
   6 images:  57 s for images, 1,678 words
  12 images:  81 s for images, 1,583 words
A 2,400-word study with 12 images takes 11 min 26 s.

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 case_study_check.py

The draft has five of the seven parts: the constraints and the reflection are missing, the role comes before the problem, and “What we measured” drops from an h2 straight to an h4. At 146 words it reads in a minute, so length is not its problem yet. The second half of the output is the budget for a full study: with 12 images, 8 minutes hold about 1,580 words, so the 2,400-word version needs about a third cut. Eight minutes is this lesson’s own limit; choose yours and keep to it. Press Edit, add the two missing sections to DRAFT and run it again.

Reading Time Calculator Paste a whole case study to see its reading time with its images, at 238 words a minute or at your own speed.

Checkpoint

Check: The seven parts and the reading time

  1. Question 1 of 2 A draft has h2 headings for The problem, My role, Research, Decisions and Outcome. Which parts are missing?

    Choose every answer that is right.

    Show the answer: Question 1 of 2

    Answer:

    • Constraints
    • Reflection

    The seven parts are problem, role, constraints, evidence, decisions and trade-offs, outcome and reflection. This draft lacks the constraints and the reflection.

  2. Question 2 of 2 At 238 words a minute, how many words fit in 8 minutes when the page has no images? Round to a whole number.

    Type a number.

    A hint

    Multiply the speed by the minutes.

    Show the answer: Question 2 of 2

    Answer: 1904 words (anything from 1903 to 1905 counts)

    238 × 8 = 1,904 words. Each image takes some of those minutes: with 12 images, the Reading Time Calculator's rule allows 81 seconds for them, which leaves about 1,580 words.

Evidence turns a claim into a case study: a participant’s words, a measure before and after, an earlier version next to the later one. Each kind is trusted for a different reason, and each has something to take out before it goes on a public page:

Evidence Trusted when Take out before publishing
A participant’s quote Short, in their words, used with consent Name, face, voice and telling details
A measure before and after It says what, over what period and from where Figures nobody agreed to share
An earlier version It sits beside the later one, with the reason Real customer data in the screens
A research artefact It shows a finding that changed a decision Notes with personal data

Consent is part of the evidence. The people in your research agreed to a purpose, and a public portfolio may not be one of them, so check what the consent form said about sharing, as the lesson on recruiting and consent sets out. The GOV.UK Service Manual advises using fully anonymised extracts, such as quotes, photos and clips, whenever possible, and notes that anonymised extracts can be shared widely, including in public. anonymisation: Removing or changing every detail in research data, direct or indirect, that could identify a participant, so that nobody can be identified from what is shared. goes further than pseudonymisation: Replacing names, addresses and other direct identifiers in research data with participant ids, so the data can be shared or analysed without re-identifying anyone. More in “Recruiting, consent and research ethics”: a quote under a participant id can still point to one person if it mentions a job title and a town, so read each extract for details like that.

Say how each number was measured

Next to every outcome, give the source and the period in a few words: “from the subscription records, over two months after launch”. A number with its source is evidence; a number without one is a claim.

Checkpoint

Check: Evidence shared with consent

  1. Question 1 of 2 Your consent form told participants their words would be used inside the team only. You want one of their quotes in your public portfolio. What do you do?
    Show the answer: Question 1 of 2

    Answer: Leave it out, or ask that participant for consent to the new use and anonymise it

    Check what the consent form said about sharing. Where it did not cover a public page, ask again or leave the quote out, and use fully anonymised extracts.

  2. Question 2 of 2 Which outcome statement would a careful reader trust most?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Unusable reports fell from 14 in 40 to 3 in 40 in the month after release, counted in the support logs.

    A number is evidence when it says what was measured, over what period and from where. Without those it is a claim.

Confidential, team and self-initiated work, labelled honestly

Much of a designer’s best work belongs to someone else, and every case has an honest way to show it. The table gives what the page should say in each:

Situation What the page says
Work under a non-disclosure agreement (NDA): A contract in which someone agrees not to share an organisation's confidential information, such as unreleased screens, data or plans, without its permission. Ask permission first. If it is refused, describe the problem and your process in general terms, give figures as ranges or changes, and show no screens
Work done in a team Each person’s part by name, and “I” for yours
Help from an AI tool What the tool did, such as drafting a summary, and what you checked or changed
A self-initiated project That it was self-initiated, and whether you tested it with anyone
A redesign of a real product That you are not affiliated with the company, that you used public information only, and no logo used in a way that suggests the company endorsed it

A label costs one sentence. A reader who learns later that a screen was a colleague’s, or that a redesign was never asked for, has a reason to doubt every other screen on the page. The developer’s version of this page, with code and READMEs, is covered in writing a project case study of the resume and portfolio track.

Checkpoint

Check: Confidential, team and self-initiated work

  1. Question 1 of 2 Your former employer refuses permission to show a project you designed. How can it still appear in your portfolio?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Describe the problem and your process in general terms, give figures as ranges or changes, and show no screens

    When permission is refused, keep the process and the reasoning, and leave out the screens and the exact figures.

  2. Question 2 of 2 You used an AI tool to draft summaries of your interview notes, and you corrected two of them. What does the case study say?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: That the tool drafted the summaries and that you checked them against the notes and corrected two

    Credit help the same way for people and tools: say what each did, and what you checked or changed.

The page itself is a design sample

A reader judges your design from the page as much as from the work on it, so hold it to the standard you would apply to a client’s page: WCAG: The Web Content Accessibility Guidelines from W3C: testable success criteria at levels A, AA and AAA. WCAG 2.2 is the current version, and content that conforms to it also conforms to 2.1 and 2.0. More in “Inclusive design and disability” 2.2 at level AA, and fast on a mid-range phone. Three habits catch common problems on a case-study page. Text reaches a contrast ratio: A number from 1:1 to 21:1 comparing the relative luminance of two colours: the lighter plus 0.05, divided by the darker plus 0.05. WCAG 2.2 sets minimums for text and for interface parts. of at least 4.5:1 (success criterion 1.4.3). Each image’s alt text: The alt attribute of an image: a short text alternative chosen by the image's purpose. An empty alt tells assistive technology to skip the image. follows its purpose, by the W3C’s alt decision tree, and a decorative image: An image that adds no information to the page, such as a divider or a background texture; it gets an empty alt attribute so that assistive technology skips it. gets an empty alt. And headings never skip a level.

For speed, web.dev sets good values at the 75th percentile of page loads: Largest Contentful Paint (LCP): A web metric: the time from when a page starts loading until the largest image, text block or video in view is drawn. 2.5 seconds or less at the 75th percentile is good. within 2.5 seconds, Interaction to Next Paint (INP): A web metric: the slowest click, tap or key press of a visit, from the input to the next painted frame. 200 ms or less at the 75th percentile is good. More in “Memory, decisions and movement behind UI rules” of 200 milliseconds or less, and Cumulative Layout Shift (CLS): A web metric of visual stability: how much visible content moves unexpectedly while a page is open. A score of 0.1 or less at the 75th percentile is good. of 0.1 or less. The program runs the image and colour checks over the published MealBox page:

Images and text colours of a case-study page, checked Python · page_check/page_check.py
"""Check the images and text colours of a published case-study page, the way a careful reader's tools would.

Alt text follows the W3C WAI images tutorial's decision tree: decorative images get alt="", informative images a
short description, images of text the text itself, and complex images (a chart, a map) a short alt with the content
also given in the page text. Missing width and height let the page shift while it loads (web.dev), a lazy-loaded
first-screen image arrives late, and text needs a contrast ratio of 4.5:1 (WCAG 2.2, success criterion 1.4.3).
The 200 KB limit for one image is this lesson's own budget, not a standard.
"""

BUDGET_KB = 200

IMAGES = [
    {"file": "hero-before-after.png", "purpose": "informative", "kb": 1840, "size": (2400, 1600),
     "first_screen": True, "lazy": True,
     "alt": "The order confirmation before and after: the new one shows the delivery day at the top"},
    {"file": "affinity-map.jpg", "purpose": "complex", "kb": 420, "size": (1600, 1000),
     "first_screen": False, "lazy": True, "alt": "affinity-map.jpg", "in_text": False},
    {"file": "divider.svg", "purpose": "decorative", "kb": 2, "size": (800, 8),
     "first_screen": False, "lazy": True, "alt": "decorative divider line"},
    {"file": "flow-v2.png", "purpose": "informative", "kb": 160, "size": None,
     "first_screen": False, "lazy": True, "alt": None},
    {"file": "quote-card.png", "purpose": "text", "kb": 90, "size": (1200, 600),
     "first_screen": False, "lazy": True, "alt": "Quote", "text": "I kept checking my email for the day"},
]

COLOURS = [  # (text, foreground, background)
    ("body text", "#374151", "#FFFFFF"),
    ("image captions", "#9CA3AF", "#FFFFFF"),
    ("links", "#2563EB", "#FFFFFF"),
]


def image_problems(image):
    alt, purpose, out = image["alt"], image["purpose"], []
    if alt is None:
        out.append("no alt attribute, so some screen readers read out the file name")
    elif alt == image["file"]:
        out.append("the alt text is the file name")
    elif purpose == "decorative" and alt != "":
        out.append('decorative, so the alt should be empty: alt=""')
    elif purpose == "text" and image["text"].lower() not in alt.lower():
        out.append("an image of text: the alt should give the text itself")
    if purpose == "complex" and not image.get("in_text"):
        out.append("a complex image: give its content in the page text as well")
    if image["size"] is None:
        out.append("no width and height, so the page shifts while it loads")
    if image["first_screen"] and image["lazy"]:
        out.append("lazy-loaded on the first screen, so it arrives late")
    if image["kb"] > BUDGET_KB:
        out.append(f"{image['kb']} KB, over the {BUDGET_KB} KB budget")
    return out


def luminance(hex_colour):
    channels = [int(hex_colour[i:i + 2], 16) / 255 for i in (1, 3, 5)]
    lin = [c / 12.92 if c <= 0.04045 else ((c + 0.055) / 1.055) ** 2.4 for c in channels]
    return 0.2126 * lin[0] + 0.7152 * lin[1] + 0.0722 * lin[2]


def contrast(foreground, background):
    light, dark = sorted([luminance(foreground), luminance(background)], reverse=True)
    return int((light + 0.05) / (dark + 0.05) * 100) / 100  # cut to two decimals, never rounded up


total = 0
for image in IMAGES:
    problems = image_problems(image)
    total += len(problems)
    print(f"{image['file']}: {'ok' if not problems else '; '.join(problems)}")
print()
for name, foreground, background in COLOURS:
    ratio = contrast(foreground, background)
    verdict = "meets 4.5:1" if ratio >= 4.5 else "below 4.5:1"
    total += ratio < 4.5
    print(f"{name}: {foreground} on {background} = {ratio:.2f}:1, {verdict}")
print(f"\n{total} problems to fix before the page goes out.")

Output

hero-before-after.png: lazy-loaded on the first screen, so it arrives late; 1840 KB, over the 200 KB budget
affinity-map.jpg: the alt text is the file name; a complex image: give its content in the page text as well; 420 KB, over the 200 KB budget
divider.svg: decorative, so the alt should be empty: alt=""
flow-v2.png: no alt attribute, so some screen readers read out the file name; no width and height, so the page shifts while it loads
quote-card.png: an image of text: the alt should give the text itself

body text: #374151 on #FFFFFF = 10.30:1, meets 4.5:1
image captions: #9CA3AF on #FFFFFF = 2.53:1, below 4.5:1
links: #2563EB on #FFFFFF = 5.16:1, meets 4.5:1

10 problems to fix before the page goes out.

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 page_check.py

Ten problems, and none needs a redesign. The hero image matters most: it is the largest thing on the first screen, so it is likely to be the element that sets the page’s LCP, yet it weighs 1,840 KB and is lazy-loaded. web.dev’s analysis found that lazy loading the images of the first screen tends to make LCP slower, so load this one at once and compress it. The affinity map needs its content in the page text, since a short alt cannot carry a chart. The flow image has neither alt text nor dimensions, and web.dev advises always giving images a width and a height so the page does not shift while they load.

Image Compressor Compress the hero image and the screenshots before you publish, then compare the sizes. Page Speed Test (Lighthouse via PageSpeed Insights) Test the published case-study page and read its LCP, INP and CLS against the values above. Color Contrast Checker (WCAG 2.2) Check each text colour against its background, such as the 2.53:1 caption grey.

Checkpoint

Check: The page as a design sample

  1. Question 1 of 2 A thin line between two sections of the case study is an image. What alt text does it get?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: An empty alt: alt=""

    Decorative images get an empty alt so that assistive technology passes over them. Informative images get a short description of what they show.

  2. Question 2 of 2 The first image on your case-study page is 1,840 KB and lazy-loaded. Which change helps the first screen most?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Load it at once instead of lazily, and compress it

    web.dev's analysis found that lazy loading the images of the first screen tends to make LCP slower; load the first image at once, keep it small, and lazy-load the rest.

Practise

The exercise hands you a first draft of a Ward Desk case-study page. Fix its parts, headings, images and colours; the sample tests apply the same rules as the two programs:

Exercise · Easy · Python

Fix a Ward Desk case-study page

solution.py holds a first draft of a case-study page for the track's Ward Desk project, as HTML in PAGE_HTML, and the page's text colours in COLOURS. Fix the draft so that:

  1. Seven parts: the page has an h2 for each of problem, role, constraints, research evidence, decisions and trade-offs, outcome and reflection, in that order. Write the reflection yourself, in one or two sentences.
  2. Headings: there is one h1, and no heading skips a level on the way down.
  3. Alt text: every image has an alt attribute chosen by its data-purpose: empty for a decorative image, and for an informative image a short description of what it shows, never a file name and never starting with "image of".
  4. Dimensions: every image has a width and a height.
  5. Contrast: every text colour in COLOURS reaches 4.5:1 on its background.
  6. Length: the page reads in 8 minutes or less at 238 words a minute, with the lesson's allowance for images.

Keep the project's facts and numbers as they are.

Starter code · solution.py

"""
A case-study page for the track's Ward Desk project, as a first draft. Fix PAGE_HTML and COLOURS so that the sample
tests pass: all seven parts as h2 headings in order, no skipped heading levels, alt text chosen by each image's
purpose (data-purpose says which), width and height on every image, text colours at 4.5:1 or more, and a reading
time of 8 minutes or less. Keep the project's facts; change the structure, the alt text and the colours.
"""

PAGE_HTML = """
<h1>Reporting a broken streetlight on Ward Desk</h1>
<p>Residents gave up on reporting broken streetlights: in a week of support logs, 14 of 40 reports had no usable
location. I was the interaction designer in a team of four, from research to the released form.</p>
<h2>The problem</h2>
<p>The old form asked for a ward number that few residents knew, so reports arrived without a place.</p>
<h2>My role</h2>
<p>I ran five sessions with residents, sketched three directions and designed the location step we shipped.</p>
<h4>Constraints</h4>
<p>The council's maintenance system accepts a street and a pole number only, and the form had to work offline.</p>
<h2>Research evidence</h2>
<p>Four of five residents looked for the pole number on the pole itself, so the form now asks for it first.</p>
<img src="pole-plate.jpg" alt="pole-plate.jpg" data-purpose="informative" width="1200" height="800">
<h2>Decisions and trade-offs</h2>
<p>We asked for the pole number before the street and dropped the map, which was slow on older phones.</p>
<img src="divider.svg" alt="divider" data-purpose="decorative" width="800" height="8">
<img src="location-step.png" data-purpose="informative">
<h2>Outcome</h2>
<p>In the month after release, reports without a usable location fell from 14 in 40 to 3 in 40, from the same logs.</p>
"""

# (what the colour is used for, text colour, background colour)
COLOURS = [
    ("body text", "#374151", "#FFFFFF"),
    ("captions", "#9CA3AF", "#FFFFFF"),
]
The sample tests · test_solution.py
"""Sample tests for the write-a-case-study-page exercise. The rules match the lesson's two examples."""

import re
from html.parser import HTMLParser

from solution import COLOURS, PAGE_HTML

PARTS = {
    "problem": ["problem"],
    "role": ["role"],
    "constraints": ["constraint", "limit"],
    "evidence": ["research", "evidence"],
    "decisions": ["decision", "trade-off"],
    "outcome": ["outcome", "result"],
    "reflection": ["reflection", "next time", "differently"],
}
WPM = 238
LIMIT_SECONDS = 8 * 60


class Page(HTMLParser):
    def __init__(self):
        super().__init__()
        self.headings, self.images, self.words, self._level = [], [], 0, None

    def handle_starttag(self, tag, attrs):
        if re.fullmatch(r"h[1-6]", tag):
            self._level = int(tag[1])
            self.headings.append([self._level, ""])
        elif tag == "img":
            self.images.append(dict(attrs))

    def handle_endtag(self, tag):
        if re.fullmatch(r"h[1-6]", tag):
            self._level = None

    def handle_data(self, data):
        self.words += len(data.split())
        if self._level is not None:
            self.headings[-1][1] += data


def parsed():
    page = Page()
    page.feed(PAGE_HTML)
    return page


def luminance(hex_colour):
    channels = [int(hex_colour[i:i + 2], 16) / 255 for i in (1, 3, 5)]
    lin = [c / 12.92 if c <= 0.04045 else ((c + 0.055) / 1.055) ** 2.4 for c in channels]
    return 0.2126 * lin[0] + 0.7152 * lin[1] + 0.0722 * lin[2]


def contrast(foreground, background):
    light, dark = sorted([luminance(foreground), luminance(background)], reverse=True)
    return int((light + 0.05) / (dark + 0.05) * 100) / 100


def test_seven_parts_as_h2_in_order():
    order = []
    for level, text in parsed().headings:
        for part, marks in PARTS.items():
            if level == 2 and part not in order and any(m in text.lower() for m in marks):
                order.append(part)
    missing = [p for p in PARTS if p not in order]
    assert not missing, f"no h2 for: {missing}"
    assert order == list(PARTS), f"the parts are in this order: {order}"


def test_one_h1_and_no_skipped_levels():
    levels = [level for level, _ in parsed().headings]
    assert levels.count(1) == 1, "use exactly one h1"
    for before, after in zip(levels, levels[1:]):
        assert after <= before + 1, f"an h{after} follows an h{before}: a level is skipped"


def test_alt_text_fits_each_purpose():
    for image in parsed().images:
        alt, purpose, src = image.get("alt"), image.get("data-purpose"), image.get("src")
        assert alt is not None, f"{src} has no alt attribute"
        if purpose == "decorative":
            assert alt == "", f'{src} is decorative: use alt=""'
        else:
            assert alt.strip(), f"{src} carries meaning: describe it"
            assert alt != src and not re.search(r"\.(png|jpe?g|svg|gif|webp)$", alt), f"{src}: the alt is a file name"
            assert not re.match(r"(image|picture|photo) of\b", alt.lower()), f"{src}: start with what it shows"


def test_every_image_has_width_and_height():
    for image in parsed().images:
        assert image.get("width") and image.get("height"), f"{image.get('src')} needs width and height"


def test_text_colours_meet_4_5_to_1():
    for use, foreground, background in COLOURS:
        ratio = contrast(foreground, background)
        assert ratio >= 4.5, f"{use}: {foreground} on {background} is {ratio:.2f}:1"


def test_reading_time_at_most_8_minutes():
    page = parsed()
    seconds = page.words / WPM * 60 + sum(max(3, 12 - i) for i in range(len(page.images)))
    assert seconds <= LIMIT_SECONDS, f"{seconds / 60:.1f} minutes to read: cut it to 8"
A hint

Run the sample tests first and fix the headings before anything else: the Constraints heading is an h4 straight after an h2, and the page has no reflection yet. Then work through the images one at a time, reading each one's data-purpose.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Then take the self-check, which covers all six ideas and moves on to harder questions as you answer:

Practice that adapts to you

Practise this lesson

13 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 13 Which three things belong on the first screen of a case study?

    Choose every answer that is right.

    Show the answer: Question 1 of 13

    Answer:

    • The result
    • Your role
    • The problem and who had it

    Problem, role and result answer what a reader asks first. Everything else supports them.

  2. Question 2 of 13 A case study opens "I began with a competitive analysis of six meal-kit apps." What is the best first fix?

    Choose one answer.

    Show the answer: Question 2 of 13

    Answer: Open instead with the problem, your role and the result, and move the analysis to the evidence

    The fix is order, not length: problem, role and result first, then the work that led there.

  3. Question 3 of 13 Your four candidates are all visual redesigns of landing pages. What is the main problem with that set?

    Choose one answer.

    Show the answer: Question 3 of 13

    Answer: They show one kind of work, so the set says nothing about research or hard trade-offs

    Choose the projects together: one weighted towards research, one towards interaction or visual design, one with a hard trade-off.

  4. Question 4 of 13 A team project looks impressive, but you cannot name two decisions that were yours. What does the lesson suggest?

    Choose one answer.

    Show the answer: Question 4 of 13

    Answer: Work out which decisions were yours before writing, and leave it out if you cannot

    Keep a team project only if you can name your decisions and their reasons, and say "I" for those.

  5. Question 5 of 13 Which part of a case study answers "What did you choose, over what, and what did it cost?"

    Choose one answer.

    Show the answer: Question 5 of 13

    Answer: Decisions and trade-offs

    Each part answers one question. Decisions and trade-offs are the part only you can explain.

  6. Question 6 of 13 A page goes from an h2 straight to an h4. Why fix it?

    Choose one answer.

    Show the answer: Question 6 of 13

    Answer: Skipped heading levels can confuse readers who move through a page by its headings

    Nest headings by rank: an h2 is followed by an h3 or another h2, never directly by an h4.

  7. Question 7 of 13 Using the Reading Time Calculator's rule (12 seconds for the first image, one second less for each next, never under 3), how many seconds do 4 images add?

    Type a number.

    A hint

    Add 12, 11, 10 and 9.

    Show the answer: Question 7 of 13

    Answer: 42 seconds

    12 + 11 + 10 + 9 = 42 seconds, which at 238 words a minute is the time of about 167 words.

  8. Question 8 of 13 Which details do you take out of a participant's quote before it goes on a public page?

    Choose every answer that is right.

    Show the answer: Question 8 of 13

    Answer:

    • A job title and town that together point to one person
    • Their name
    • Their face in a video clip

    Anonymise extracts fully: remove direct identifiers and the details that together point to one person.

  9. Question 9 of 13 How do you show an earlier version of a screen so that it counts as evidence?

    Choose one answer.

    Show the answer: Question 9 of 13

    Answer: Beside the later version, with the reason it changed

    Iterations become evidence when the reader sees both versions and the finding that changed one into the other.

  10. Question 10 of 13 You redesigned a well-known travel app on your own. What must the case study say?

    Choose one answer.

    Show the answer: Question 10 of 13

    Answer: That it is self-initiated, that you are not affiliated with the company and that you used public information only

    A redesign of a real product is fine when it says plainly what it is and what it is not.

  11. Question 11 of 13 Two designers worked on the screens of one project. How should your case study describe them?

    Choose one answer.

    Show the answer: Question 11 of 13

    Answer: Name the other designer's part, and say "I" for the screens and decisions that were yours

    Credit people by their part, and use "I" for your own decisions.

  12. Question 12 of 13 What contrast ratio does WCAG 2.2 success criterion 1.4.3 ask for body text at level AA? Give the first number of the ratio.

    Type a number.

    A hint

    Large text needs less.

    Show the answer: Question 12 of 13

    Answer: 4.5

    4.5:1 for text of normal size, and 3:1 for large text. The ratio is not rounded, so 4.49:1 does not meet it.

  13. Question 13 of 13 Your case-study page jumps as its images load, and its CLS is 0.3. What is the first thing to check?

    Choose one answer.

    Show the answer: Question 13 of 13

    Answer: Whether every image has a width and a height, or a reserved aspect ratio

    CLS of 0.1 or less is good. Give each image its width and height so that the browser keeps space for it while it loads.

Interview questions

Warm-up: how many case studies should a UX portfolio have, and how do you choose them?

Two to four, each strong enough to present for 15 minutes and to defend under questions. Choose them together, not one by one: between them they should show research, interaction or visual design, and at least one hard trade-off forced by a constraint. Prefer recent work and at least one project with real users. A course project or a self-initiated one is fine if it is labelled as such. Leave out a project if you cannot name two decisions that were yours, because a case study is a record of decisions and the reader is there to learn about you.

Warm-up: what belongs on the first screen of a case study?

The problem and who had it, your role, and the result with where its number comes from. Those three answer what a reader asks first: is this relevant, what did you do, did it work. Everything else, the research, the sketches, the iterations, explains how you got there and belongs further down, where an interested reader will look for it.

Two questions with model answers come from this lesson. Each asks how you would handle your own portfolio under pressure; settle on your opening sentence for each before you read the step that follows it:

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Fresher to senior

    Your case study says a redesign "cut support calls by 40 %". In an interview a design lead asks how that was measured, and you realise the figure was a manager's estimate in a meeting. What do you say now, and what do you change on the page?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Fresher to senior

    Your strongest case study runs to 4,800 words with 30 images. A design lead you hope to work with says they give each portfolio about five minutes. How do you cut it, what survives, and how do you check the result?

    Model answer, Pro. First step and follow-ups

Flashcards · 8 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. What should the first screen of a case study carry?

    The problem, your role and the result, with where the number comes from. Readers decide from the first screen, so a list of process stages or a persona before the problem tells them the method but not why the work mattered; the process belongs in the middle.

  2. How many projects belong in a UX portfolio, and how are they chosen?

    Two to four that together show process, judgement and outcomes and differ from each other, such as one weighted towards research, one towards interaction or visual design and one where a constraint forced a hard trade-off. At least one should have had real users.

  3. What are the seven parts of a case study?

    Problem, role, constraints, evidence, decisions and trade-offs, outcome and reflection, each answering a question the reader has. A short part is better than a missing one.

  4. When is an outcome in a case study evidence rather than a claim?

    When it says what was measured, over what period and from where, such as from the subscription records over two months after launch. A number without its source is a claim.

  5. What must be taken out of research evidence before a case study is published?

    Names, faces, voices and telling details from participants' quotes, figures nobody agreed to share, real customer data in screens and notes with personal data. Quotes are used only with consent.

  6. How does a case study stay honest about team work, AI help and confidential work?

    Name each person's part and use I for yours; say what an AI tool did and what you checked or changed; for work under a non-disclosure agreement, ask permission first and, if it is refused, describe it in general terms with figures as ranges and no screens.

  7. What must a self-initiated redesign of a real product say?

    That it was self-initiated and whether you tested it with anyone, that you are not affiliated with the company and used public information only, and it uses no logo in a way that suggests the company endorsed it.

  8. Which checks does a case-study page itself need to pass?

    WCAG 2.2 AA: text at 4.5:1, alt text that follows each image's purpose, empty alt for decoration, and headings that never skip a level. For speed, at the 75th percentile: LCP within 2.5 s, INP of 200 ms or less and CLS of 0.1 or less, so the first-screen image is not lazy-loaded.

Key takeaways

  • Readers decide from the first screen, so a case study opens with the problem, your role and the result.
  • Two to four projects that together show process, judgement and outcomes beat a longer list, and at least one should have had real users.
  • A case study has seven parts, problem, role, constraints, evidence, decisions, outcome and reflection, and a short part is better than a missing one.
  • Evidence is trusted when it says where it came from, and research extracts go public only with consent and after anonymisation.
  • Confidential work, team work, work done with AI help and self-initiated work stay honest with one clear sentence on the page.
  • The page is itself a sample: text at 4.5:1, alt text by purpose, headings in order, and a page that is fast on a phone.

Terms in this lesson

anonymisation
Removing or changing every detail in research data, direct or indirect, that could identify a participant, so that nobody can be identified from what is shared.
case study
A written account of one design project for a reader who was not there: the problem, your role, the constraints, the evidence, the decisions, the outcome and a reflection.
Cumulative Layout Shift (CLS)
A web metric of visual stability: how much visible content moves unexpectedly while a page is open. A score of 0.1 or less at the 75th percentile is good.
decorative image
An image that adds no information to the page, such as a divider or a background texture; it gets an empty alt attribute so that assistive technology skips it.
Largest Contentful Paint (LCP)
A web metric: the time from when a page starts loading until the largest image, text block or video in view is drawn. 2.5 seconds or less at the 75th percentile is good.
non-disclosure agreement (NDA)
A contract in which someone agrees not to share an organisation's confidential information, such as unreleased screens, data or plans, without its permission.
self-initiated project
Design work done on your own initiative, with no client or employer behind it, such as a redesign of a public product; shown in a portfolio with that label.
UX portfolio
A small set of case studies that shows how a designer works: the problems, the decisions and their reasons, and what changed as a result.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.