Last Updated: 2024-11-21
These are some notes from the excellent Refactoring UI Book. I highly recommend you buy a copy.
The general problem
- Problem with CSS classes like
.article-car
is that a future class like .staff-member-card
might duplicate the styles. So you go for something generic like .card
... until...
- You find that the problem with a supposedly reusable
.card
is you end up a card-like design that is almost but not exactly the same.
- Therefore he recommends using Tailwind/Tachyons classes that describe css properties such as
.align-right, .padding-sm-horizontal bg-blue-500
etc. but composed into either larger components (e.g. via rails partials or JSX components etc.)
General ideas
- Design in gray-scale first, before adding color
- Using much larger size differences for importance is less readable than using bold / lighter text colors
- Have systems for margins/padding/color/width/height/box shadows/line-height so you don't deliberate too much on variants all the time
- Design by process of elimination (e.g. have only three possible icon sizes - 12/16/24 and choose between them)
- Rounded corners are more playful (more rounded, more playful). Straight edges are formal (e.g. investment platform)
- Serif fonts are formal. sans serif are more playful (with rounded sans serif most playful)
- Design the actual feature first (e.g. the search form). Don't worry about your website's navigation shell. In fact, you probably will never need this (see Google's simple sites!)
- Don't use gray against colored backgrounds (only against white backgrounds). This is because we use gray to create hierarchy (i.e. to de-emphasize). This only works if you bring the color of the font closer to the background (i.e. make the font more white!). The fact of it being gray on its own is irrelevant. Therefore, if you want to de-emphasize against a color background, hand-pick a color with the same hue.
- If you use low opacity on fonts above a textured background, the background will show through and this is nearly always ugly.
- It is easier to emphasize something by de-emphasizing other things (e.g. if coloring the important thing purple doesn't make it pop enough, also make the other links gray). OR: if a sidebar is competing with main content area, try giving the sidebar the same background color as the overall page (e.g. a faded gray) and the main content area a bright white background.
- Don't use labels so often (e.g. Name: Jack, Email: jack@example.com) - people will figure this stuff out usually. iI not, add clarifying text "e.g. 12 left in stock" is nicer than "In stock: 12". If a label truly is necessary, put it in the background (without the colon and with a faded color to de-emphasize)
- e.g. top line "Heart Rate" (gray and faded), next line (bigger and bold) "82BPM"
- Keeping the browser default of h1 = "huge font" down to h6 = "small font" isn't helpful for UIs. It only works for articles. A H1 tag for "Manage Account" at the top of the page makes perfect semantic sense, but ultimately this is just a label so it need not be larger than the actual important content. Generally don’t let the element you’re using influence how you choose to style it.
- Bold feels emphasized because it takes up more surface area.
- Icons, due to their large surface area, are basically automatically emphasized. Since you can't change their weight like you would fonts, use color (e.g. gray) to reduce prominence
- If making a border a darker color makes the design harsh and noisy, try instead keeping it light but increasing weight (i.e. making it wider)
- Every action has higher or lower importance. Making your delete button huge and red gives it too much importance because it pops. If it is a tertiary action, it should be unobtrusive but discoverable (e.g. just a link).
- Expanding on this: Primary actions could have solid high contrast backgrounds (e.g. big green button). Secondary could use outline style (i.e. the font itself in a primary color again a muted, washed out background color that is a variant to the font) or lower contrast background.
- Start with what you think is way too much space. It nearly always helps a design. So start with too much and gradually remove. (Instead of my old default of gradually adding space until it looks OK)
- Your spacing/sizing system needs to be non-linear (i.e. "multiples of 4px" will not be any good since scaling a button is not the same as scaling a 500px media card)- e.g. going up a size here linearly would cause your buttom to jump 33% in size but the media card only 4%
- start with a sensible base value (16px is great since it divides nicely and is default in many browsers for fonts). The whole range of values then could be: (tailspin also has something like this built-in):
bash
-4
-8
-12
-16
-24
-32
-48
-64
-96
-128
-192
-256
-384
-512
-640
-768
- One pro of this system: type in these numbers while in browser / sketch to fiddle with a design. if somethings looks off, try the next value in the scale
- Pro-tip: you don't have to fill the screen. if you only have 512px of content, just center that and be done with it. Or keep your nav bar and footer full width but leave this bit smaller!
- If something, e.g. a long form, feels unbalanced, try adding columns. E.g. you could have sparse supporting text in the left column (e.g. "Basics" plus an explanation about what these form fields are about)
- The downside to grids occurs when resizing the screen. Do you really want the sidebar links to take up 25% (say) of the larger screen? Isn't this space better used with the main content? Perhaps a fixed width, optimized for the sidebar's contents is better.
- More generally (even inside components): don't use % to size, unless you are sure you want it to scale. E.g. having an image scale up may look nasty.
- If, when using a grid, you make a component (say the login form in screen center, the only component on the screen) take up less columns on certain screen sizes (e.g. 6 cols on tablets instead of 8 on desktop), you can get into the state where it ends up squished below its optimum size (e.g. 500px) despite there being plenty of space available on screen (i.e. the component ends up bigger on medium screens than large screens). The solution is to use a max-width and only force components to shrink when screen is smaller than this.
- Using em units for fonts does not mean it'll look OK in mobile. e.g. headline at 2.5em could look great on desktop, but would be way too big on mobile (i.e. 35px against base of 14). In general a good headline size for small screens is 20-24px - i.e. 1.5-1.7 times the 14px base
- In fact, avoid ems - [jack: need to double check: paraphrasing] ems multiply in nested situations. e.g. with the base 16px, setting font to 1.25em is 20px. But any element set to 1em in this nexted context is now 20px (since an em is defined as the font pont size). Instead use px or REM (rem= ROOT em)
- In general: elements that are large on big screens need to shrink faster than smaller elements.
- Consider padding on buttons: if the padding scales at the same rate as the font-size, yes, the button scales proportionally on smaller screens. But do you really want that? Usually you want less padding on the smaller screen relative to text
- Avoid ambiguous spacing - i.e. when margin above and below a form label is set
to the same amount, it's not super clear from a design perspective what content goes where (into what clusters). But if you give it double the margin above as below, it's much clearer. (20px vs 10px)
- A modular type scale is not ideal (e.g. golden ratio) because there are insufficient values and also fractional entities. Instead handpick values for your scale. He recommends: 12, 14, 16, 18, 20, 24, 30, 36, 48, 60, 72
- When in doubt about your own font taste, use the system stack : -apple-system, Segoe UI, Roboto, Noto Sans, Ubuntu, Cantarell, Helvetica Neue; at least users are familiar with them. Or use the most popular ones (wisdom of the crowd). Or steal from a site you love.
- Never choose a font without lots of variant styles/weights
- Fonts that have tight letter spacing and short x heights are not that legible at small sizes so should be reserved for headlines.
- Paragraphs should be narrower to be readable. Ideally about 45-70 chars per line, achievable with 20-35 em units.
- Also, limit paragraph width even if the ensuing parts of the page are full width (e.g. three columns with images following the introductory paragraph)
- let the introductory paragraph be less wide (e.g. centered and 2 columns wide)
- Always align mixed font-sizes (e.g. three items across a row) with
align-items: baseline
instead of center. This takes advantage of an alignment reference your eyes already see.
- Coloring links can look really annoying, esp. in interfaces where basically everything is a link. instead emphasize with a heavier font weight or darker color. don't even do this for ancillary links: instead just add an underline when hovering or something.
- Generally,
text-align: left;
everything.
- Center align is great for headlines or short, independent blocks of text. But once it gets beyond about two lines, it looks cleaner if left-aligned.
- Often the easiest way to fix alignment issues where one item is too big is to reduce amount of text
- Right align numbers in tables. Why? Because the decimal is always in the same place!
- Never use justified text without hyphenation (
hyphens: auto
) - otherwise there are awkward gaps
- Generally letter spacing should be left to the font-designer. But if you use a font for purposes other than which it was intended (e.g. a copy font for headlines) it could make sense to tweak (e.g.
letter-spacing: -0.05em;
) to reduce the headline-like wide letter spacing.
- Another use-case for letter spacing is all caps text. This is because when all letters are forced to the same height (compare to lowercase where top of h ascends and bottom of p descends) then there is less to distinguish characters. Therefore increasing letter spacing helps
letter-spacing: 0.05em;
- Prefer HSL to RGB. Why? Because similar colors look similar in code. hue represents color's position on wheel. Measured in degrees. Saturation is vividness/intensity (0 gray - no color, 100% full vivid). Lightness - 0 is black 100% is white.
- Be careful: the same saturation value at 50% lightness looks more colorful than at 90% lightness or 10%. Therefore use an x2 curve to increase saturation as something goes further away from 50% lightness in EITHER direction.
- Some hues are perceived as brighter than others (e.g. yellow vs purple). Therefore a mechanical approach to brightness shades, applied across hues, would be naive unless it took this into account. Also this means you can make something brighter not just by increasing light (which has the side effect of making it less vivid and more washed out) but by rotating hue.
- The idea of using 4 colors only from a color scheme website will lead you to have sites with too much primary colors. To the contrary you need far more colors
- color category 1: grays. you'll want at least 5-10 shades of gray
- Color category 2: primary color. Usually one, maybe two. But with 5-10 shades (e.g. for alert backgrounds etc.)
- Color category 3: one or more accent colors. Something eye-grabbing - yellow/pink/teal
- Color category 4: state emphasise - red for danger, yellow for warning message, green for positive trend etc. again, some shades help! These colors should be used very sparingly
- Define ALL these shades up front. Have a nice page where they are available. Do it manually rather than with fancy
lighten
functions. Start with your base color and make lighter or darker variants. What is your base color? Probably what would look pretty in a button. The darkest shade of your color is probably what would be used for text. The lightest might be used to tint the background of an element. Try building an alert component with text, demphasized text, tint background, and border. This should use four colors. Finally, fill in the gaps between these colors
- True gray has 0% saturation. But don't do this since you miss out on cool vs. warm grays. Gray grays are saturated with some blue, warm with yellow/orange.
- In order for white text to be nicely contrasted against a colored background, the background must usually be very dark. this has the side effect of drawing attention to the element and potentially making it jump to a higher place in the hierarchy. When you don't want this, flip the contrast - use a light color background and dark colored text.
- Don't rely on color alone to communicate something (e.g. price up or down). use icons too. This helps color blind people. Also use contrast - light and dark differences are easy for color blind people to tell apart.
- If there's a risk of images clashing (image on top blending into one in background), apply an "invisible border" around the image perimeter - i.e. one the same color as the background color.
- How to put text over images and get it to remain readable? either a) add a
semi-transparent overlay (e.g. a black overlay with alpha of 0.55 will make
white text placed over the overlayed image readable); or b) lower the contrast of
the image. If you do this, you'll need to compensate by adjusting the
brightness up; or c) in a photo editor, "colorize" the image with a color
(desaturate then add a solid fill with blend multiply); or d) add a text shadow
(improves contrast) - use large blur and no offset
text-shadow: 0 0 50px
(you probably want to combine this with point b and lower the image
contrast... but you won't have to go as far in reducing it if you also have
shadows)
- If you want to scale up an icon and it looks crappy naturally, try enclosing it in a circle with a tinted shade. This keeps the icon looking good while still filling the larger space.
- Never scale down screenshots - impossible to read/too cramped. The 16px font in your app becomes 4px in your screenshot! Instead take a screenshot of the design at a smaller screen size (e.g. tablet layout) or limit to specific area
- Never display user-uploaded images at their intrinsic ratios - wreaks havoc on your structure. Instead center inside fixed image containers, cropping out whatever does not fit. Use
background-size: cover
- If an image has a similar color to your background, it's called background bleed and the image loses its shape. A outer border looks like shit, so solve with a subtle inner shadow:
box-shadow: inset 0 0 0 1px hsla(212, 15%, 72%)
- Often, changing the bullet-icon is a nice way to add character - e.g. each item could be a lock for security benefits or a pretty blue checkbox for features
- Link underlines that partially overlaps text is pretty and fresh. Also slight bolding looks great.
- Custom checkboxes in forms really adds visual value
- Add colorful accent border across the top of a card (e.g. 3px blue) to add visual accent. Can work to the left of an alert too! Or under the first 30px of your 200px headline. Or across the top of your whole layout.
- Background colors add lots of excitement, especially with a gradient (with two hues no more than 30degrees apart)
- Try adding a strip of a different background color across your card to subtly draw attention to some information
- A subtle repeating pattern in the background is also great. You might use just a small amount of this perhaps. Keep the contrast low to avoid interfering with legibility and hierarchy.
- Alos try importing an image with simple geometric shapes (e.g. a triangle of small dots)
- Use fewer borders. They can cause busy-ness and clutter. When you want separation between items (e.g. a card item floating above a background) try the more subtle box-shadow. Or give the bordering elements different background colors - this naturally creates distinction. or add more spacing.
- Try selectable cards over radio buttons
Rabbits holes
Depth
- Golden rule: light comes from above. So the top edge of something raised is brighter and bottom one is darker (since it has the shadow of the raised item)
- If you have shadows at the top of an element (e.g. imagine a kitchen cabinet) then it will look inset (since the lip above is blocking the light)
- Syntax for box-shadow
/* offset-x | offset-y | blur-radius | color */
box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2)
- So: say you have a blue button that ought to be raised. A top border or an inset box shadow with a slight vertical shadow in a lighter shade would achieve this effect
box-shadow: inset 1px 0 hsl(..)
. Underneath it needs a shadow with a slight vertical offset box-shadow: 0 1px 3px
.
- Chill with blur radius ...
- For something you want to appear inset/depressed, add a slightly brighter shade to the bottom with a negative vertical offset
box-shadow: 0 -2px 0 hlsa
and a darker shade to the top box-shadow inset 0 2px 2px
- Text inputs and checkboxes should generally appear inset
- Buttons should only use small shadows, but larger elements like dropdowns can use larger shadows with larger blur radii to simulate a Z axis e.g.
box-shadow 0 4px 6px
. Modal dialogues would then have huge shadows: box-shadow: 0 15px 35px
- Have a fixed number of shadows in your system to speed up design- 5 is sufficient. Start with biggest and smallest (modal vs button) and fill in in-between
- Shadows also help with interaction. E.g. adding a shadow when an element is clicked makes it pop forward. Can make it clear user can drag it, for example. Or reducing/removing a shadow when a button is pressed.
- Again: key is to think in terms of Z-axis.
- Another thing to consider is having two shadows: one is darker and tighter with a small blur radius - that's literally underneath the object (e.g. under a flower pot) vs a lighter broader blurrier one (e.g. cast by blocking light). In the lingo, the first is ambient light, the second is direct light.
- Depth can also be communicated without shadow - e.g. color. a lighter background (say white) against a duller background (say gray) will look raised off the page.
- A "solid shadow" is one without a blur radius. It makes something stand off the page without sacrificing the flat aesthetic.
- Having an element (e.g. a flight search bar) overlap both the top background (a city) and the bottom one (white space), makes it clearly look on top of both. Ditto for making something (e.g. a plan upgrade) taller than the rest.
Line Height best practices
- Its purpose is to make it easy for the reader to find the next line when the text wraps (so as to avoid re-reading the same line - a risk if line-height is too low)
- Your line-height and paragraph width should be proportional — narrow content can use a shorter line-height like 1.5, but wide content might need a line-height as tall as 2.
- Also, when using a small font, you should increase line-height (at tiny sizes, it's gonna be extra hard for your eyes to find the right line). 1.75 could be the low-end here, instead of 1.75. But if text is huge, you'll have little difficulty, a line height of 1 is perfectly fine (and 1.5 would look WAY too space-y)
- In list items, the margin between li items should be larger than the line height of the items. Say you have a multi-line bullet point - it should be clear when next bullet begins
Margin best practices
- Use more margin above than below (e.g. 1.5/double/triple) to separate items
- The same thing applies horizontally - i.e. there should be very little horizontal spacing between the thumbs up icon and the like count (since these actions are similar and should be clustered together) but much more between the comment icon to the right.