Theory

Links, images and colour

Your page is semantic and styled. This lesson is about the details that decide whether it's pleasant or painful to actually use — how links behave, how images load, and whether the colours can be read.

Every href and src is a path, and there are three kinds:

  • Relativestyle.css, ./style.css, ../images/logo.png. Resolved from the current file's folder. Your <link href="style.css"> works because the stylesheet sits right next to index.html.
  • Absolutehttps://apod.nasa.gov/apod/image/1604/…jpg. The full address of something on another server. Your card images are absolute because they live on NASA's servers, not yours.
  • Root-relative/about.html. Starts from the site's root, ignoring the current folder. Useful once a site has many pages in many folders.

The rule of thumb: relative for your own files (the site can move to any domain and still work), absolute only when you're pointing at another server.

A link is not one appearance but several, and CSS lets you style each:

  • :hover — the pointer is over it.
  • :focus-visible — it has keyboard focus and the browser thinks a ring should show (i.e. the user is navigating by keyboard, not clicking).
  • :visited — the user has been there before.

The important distinction is :focus versus :focus-visible. :focus matches whenever an element has focus — including right after a mouse click, which is why a plain :focus outline makes a ring flash on every click. :focus-visible matches only when a ring is actually useful: keyboard navigation. So mouse users get a clean click, keyboard users get a clear ring. Style :focus-visible, not :focus.

One detail that quietly decides whether the ring is even visible: outline-offset. A positive offset pushes the ring off the control onto the page behind it; 0 paints it on the control's own edge. The rule is outline-offset: 2px so the ring sits on the page and stays clearly separated from the button — you'll add exactly that next.

Images: three attributes that matter

Every <img> on your cards carries three things, and each earns its place:

<img src="…/DustAngelNebula_rba1024.jpg" alt="A Dust Angel Nebula"
     width="1024" height="664" loading="lazy">
  • alt — what the picture shows, for anyone who can't see it (a screen-reader user, or a browser that failed to load the image). Describe the content or function, not the file: alt="A Dust Angel Nebula", never alt="image" or alt="DustAngelNebula_rba1024.jpg". If a picture is purely decorative, give it an empty alt="" so a screen reader skips it — an empty alt is a decision, a missing alt is a bug.
  • width and height — the picture's real pixel size. The browser uses the ratio to reserve the space before the image loads, so text doesn't jump down the page when it arrives. (These three images are 1024×664, 500×743 and 640×480 — wildly different shapes — yet object-fit: cover still renders them as uniform tiles.)
  • loading="lazy" — don't fetch the image until it's about to scroll into view. On a long gallery, that's the difference between downloading three pictures and downloading thirty.

srcset: the right idea, the wrong inputs here

There's a fourth attribute you'll read about — srcset — for serving a different-sized file to different screens. The syntax is real:

<img src="…/DustAngelNebula_rba1024.jpg"
     srcset="…/DustAngelNebula_rba1024.jpg 1024w,
             …/DustAngelNebula_rba.jpg 2400w"
     sizes="280px" alt="A Dust Angel Nebula">

The 1024w is honest — the filename says so. The 2400w is a guess: NASA gives a display copy and a full-resolution copy but never publishes the full-res width. And a wrong width descriptor doesn't fail quietly — the browser trusts it, so on a high-density phone it would happily download the multi-megabyte full-res photo to fill a 280-pixel tile. The page gets heavier, on exactly the device where weight hurts most, because of a line you added to make it lighter.

So srcset stays out of your cards. This is the whole lesson in miniature: the tool is right, the inputs are wrong, and shipping it anyway would be worse than not using it. And the deeper reason srcset can't help here is there is no size small enough — a display copy and a full-res copy are both far bigger than a 280px tile. A real project would generate tile-sized versions at build time or through an image service (something like Cloudinary or imgix); NASA ships a picture to look at, not a set of thumbnails.

Colour you can actually read

Recede text by choosing a muted colour you can measure, never by fading it. Contrast is a ratio between text and its background; body text needs 4.5:1.

Measure your muted grey in the dev tools. In the dark theme, --text-muted (#8b93b0) is 6.32:1 on the page and 5.67:1 on a card — both clear 4.5:1. Switch to the light theme and its own muted grey (#5c6480) measures 5.46:1 and 5.85:1 — also clear. Good so far.

Now the trap. The light theme defines its own muted grey on purpose. If it had lazily reused the dark theme's #8b93b0, that grey on the pale background measures 2.84:1 — a clear fail; the dates would be a ghost. A colour that passes in one theme can fail in the other, and the only fix is a per-theme value — which is exactly why the stylesheet has a separate [data-theme="light"] block. This is why you measure the colour that actually lands on screen, in both themes, rather than trusting one.

Never colour alone

Both the nav links and the inline "Astronomy Picture of the Day" link are marked only by being a different colour from the text around them. For a reader who can't distinguish that colour, the inline one disappears into the sentence. The nav gets away with it because its position — a row of pills at the top — already says "these are links." An inline link in a paragraph has no such signal, which is why many teams underline inline links always. The rule: never let colour be the only thing carrying meaning. Pair it with an underline, an icon, a label, or a shape.