display: inline-block
Besides block, inline, and flex, there is one more value — inline-block. It combines the behavior of inline and block elements.
What comes from where?
| From inline elements | From block elements |
|---|---|
| Elements sit on the same line | You can set width and height |
Without width, the width is defined by the content |
padding and margin work fully |
Positioned via text-align |
Not split into parts when the line wraps |
<button> element has display: inline-block by default. That is why you can center it with text-align: center on its parent.Centering a button
.form-row:last-child {
text-align: center; /* the button inside becomes centered */
}
The gap between inline-block elements
A gap (~4px) appears between inline-block elements. It is not “created” — it comes from the line-break character or space in your HTML code.
How does it appear?
<!-- There is a line break between these divs → the browser shows 1 space -->
<div class="card">...</div>
<div class="card">...</div>
How to remove it?
| Method | Drawback |
|---|---|
| Write tags with no spaces between them | Fragile solution — any space breaks the layout |
font-size: 0 on the parent, restored on the children |
Breaks relative units (em), inconvenient |
| Flexbox instead of inline-block | Best solution — no hacks |
display:
flex instead of inline-block. Flex items have no gaps between them.Dropdown list: <select> + <option>
<select> is a form element for choosing one option from a list. <option> is each choice (it can only appear inside <select>).
<label>
<span class="form-hint">Choose a city</span>
<select class="select" name="city">
<option>Kyiv</option>
<option selected>Lviv</option>
<option>Odesa</option>
<option>Kharkiv</option>
</select>
</label>
| Attribute | Purpose |
|---|---|
selected |
Marks the option selected by default |
multiple |
Allows selecting several (rarely used — awkward UX) |
selected, the first <option> becomes selected. If selected is on several, the last one in the code wins.When to use select vs. radio?
- More than 5 options
- Takes up minimal space
- Requires 2 clicks (open + choose)
- 2–5 options
- All options visible at once
- Choice in 1 click
appearance: none — resetting the styling
Browsers have their own styling for form elements (<select>, some <input> types) that is hard to override. The appearance property with the value none disables this default styling.
.select {
-webkit-appearance: none; /* Safari, old Chrome */
-moz-appearance: none; /* Firefox */
appearance: none; /* standard */
}
appearance:
none without prefixes. But for full compatibility, add all three lines.Where to apply it?
<select>— removes the default arrow, allows full stylinginput[type="search"]— removes the non-standard styling in Safari
appearance: none everywhere. Buttons and ordinary <input>s style perfectly well without it.background — the element’s background
A group of properties for controlling the background: color, image, repetition, position, size.
background-color
Sets the background color. It is recommended to always place a color under the background-image — in case the picture fails to load.
background-image
background-image: url("icons/arrow.svg");
Loads an image and shows it as the element’s background. Without extra settings, it repeats and fills the whole block.
background-repeat
| Value | Behavior |
|---|---|
repeat (default) |
Repeats horizontally and vertically |
repeat-x |
Horizontally only |
repeat-y |
Vertically only |
no-repeat |
No repetition (a single copy) |
no-repeat is used most often — for icons, decorative elements, and unique background images.background-position
Defines the position of the background image. Two values: horizontal and vertical.
/* 9 anchor points */
background-position: left top; /* top-left corner */
background-position: center center; /* strictly centered */
background-position: right center; /* right, vertically centered */
/* With an offset from the anchor point */
background-position: right 10px center; /* right, -10px to the left */
right
center with an offset to the left.background-size
| Value | Behavior |
|---|---|
200px auto |
Width 200px, height — proportional |
auto 100px |
Height 100px, width — proportional |
cover |
Covers the entire block (part of the image may be cropped) |
contain |
Fits fully into the block (part of the block may be empty) |
contain — for images where it’s important to show everything (portraits, diagrams).
Styling <select>: a full example
After resetting with appearance: none, the <select> element becomes an ordinary rectangle. Now we add our own arrow via background-image:
.select {
display: block;
width: 100%;
padding: 10px 35px 10px 15px; /* more padding-right for the arrow */
border: 1px solid #ccc;
border-radius: 4px;
font-size: 16px;
font-family: inherit;
box-sizing: border-box;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-image: url("icons/arrow-down.svg");
background-repeat: no-repeat;
background-position: right 10px center;
background-size: 12px auto;
}
<select> in a <label> — so that clicking the caption activates the field. And add :focus styles identical to your other fields.Radio buttons: input type=”radio”
Radio buttons let you choose one option from a group. The name “radio” comes from old radio receivers: you press one button and another pops out.
<label>
<input type="radio" name="gender" class="radio">
<span class="radio-text">Male</span>
</label>
<label>
<input type="radio" name="gender" class="radio">
<span class="radio-text">Female</span>
</label>
The name attribute — creating a radio group
Without name, each radio button works independently — you can select them all at once. The name attribute binds radio buttons into a group: only one in the group is selected.
name value should reflect the group’s purpose: "gender", "subscription", "delivery" — not the abstract "radiogroup".<label> — then clicking the text also activates the choice. Plus, <label> is an inline element, so the radio buttons will automatically stay on the same line.Pseudo-elements ::before and ::after (practice)
Pseudo-elements let you add virtual blocks to any element — without changing the HTML code. They are created exclusively through CSS.
The required content property
.heading::before {
content: ""; /* required! without content, the element won't appear */
display: block;
width: 60px;
height: 4px;
background-color: #4caf50;
margin-bottom: 10px;
}
Limitations
Pseudo-elements do not work on these tags:
<input>— self-closing, no content<textarea><select><img>— self-closing
Layer priority
::before appears before the content and has lower priority (the content overlaps it). ::after appears after the content and has higher priority (it can overlap the content).
vertical-align — vertical alignment
Works for inline and inline-block elements. Defines how the element aligns relative to the line.
| Value | Behavior |
|---|---|
baseline (default) |
Along the text baseline (the bottom edge of the letters) |
top |
Along the top edge of the line |
middle |
Along the center of the line |
bottom |
Along the bottom edge of the line |
vertical-align: middle is perfect for aligning icons, radio buttons, and checkboxes next to text.The + (adjacent) and ~ (general sibling) selectors
These selectors work between “siblings” — elements at the same nesting level (one parent). They are the analog of the > (child) and space (descendant) selectors, but horizontal.
The + (adjacent) selector
Styles an element if its immediate older sibling matches the condition:
/* Style .radio-text if it is immediately preceded by .form-hint */
.form-hint + .radio-text {
color: red;
}
The ~ (general sibling) selector
Styles an element if any of its older siblings matches the condition:
/* Style .radio-text if there is a .form-hint somewhere earlier in the code */
.form-hint ~ .radio-text {
color: red;
}
Comparison of all four “horizontal-vertical” selectors
| Selector | Direction | Strictness |
|---|---|---|
A B (space) |
Parent → descendant (any depth) | Loose |
A > B |
Parent → immediate child | Strict |
A ~ B |
Older sibling → any younger one | Loose |
A + B |
Older sibling → immediate next one | Strict |
~ selector is more reliable than +: if someone inserts an extra element between the siblings, + breaks, but ~ does not.Custom styling of radio buttons
Standard radio buttons cannot be styled: browsers don’t allow changing their color, size, or shape. The solution is to create a mock and hide the original.
Step 1: create a mock via ::before
Since <input> doesn’t support pseudo-elements, we create them on the neighboring <span>:
.radio-text::before {
content: "";
display: inline-block;
width: 20px;
height: 20px;
margin-right: 8px;
vertical-align: middle;
background-image: url("icons/radio.svg");
background-repeat: no-repeat;
background-position: center;
background-size: contain;
}
Step 2: pass the :checked state via the ~ selector
/* If the radio button is selected → swap the picture on the mock */
.radio:checked ~ .radio-text::before {
background-image: url("icons/radio-active.svg");
}
::before of the .radio-text element if somewhere before it there is a .radio in the :checked state.Step 3: hide the original accessibly
.radio {
position: absolute;
width: 1px;
height: 1px;
clip-path: inset(50%);
overflow: hidden;
}
display: none or visibility: hidden — the element would become inaccessible to screen readers and keyboard navigation. clip-path: inset(50%) hides it visually but preserves accessibility.Step 4: the focus state
/* Focus from the radio button → visual highlight of the text */
.radio:focus ~ .radio-text {
outline: 2px solid #1976d2;
outline-offset: 2px;
}
position: absolute — briefly
position: absolute pulls an element out of the normal document flow and places it on a separate “layer”:
- Other elements behave as if the absolute element doesn’t exist
- Width and height are calculated from the content (as in inline-block)
- You can set
widthandheight— they will work - The parent element doesn’t account for the height of the absolute child
position in detail (static, relative, absolute, fixed, sticky) is covered in later lectures. Here it is only needed to correctly hide the original radio/checkbox.clip-path: inset() — visual clipping
The clip-path property lets you show only part of an element and hide the rest.
/* Clip 50% from each side — the element disappears visually */
clip-path: inset(50%);
/* Clip into a circle shape */
clip-path: circle(50%);
/* Clip into a polygon shape */
clip-path: polygon(50% 0%, 100% 100%, 0% 100%);
inset(50%) — clips 50% from each side of the element. The remaining area = nothing. The element disappears visually but stays accessible to screen readers.clip: rect() property did the same, but it only worked with position:
absolute and is deprecated. Use clip-path — it is more modern and supports more clipping shapes.Checkbox: input type=”checkbox”
A checkbox is a “yes/no” element: to agree or not agree. Essentially, it is two radio buttons in a single element.
<label>
<input type="checkbox" name="consent" class="checkbox" required>
<span class="checkbox-text">I agree to the processing of personal data</span>
</label>
The styling is absolutely identical to radio buttons:
- Create a mock via
::beforeon.checkbox-text - Pass the
:checkedstate via.checkbox:checked ~ .checkbox-text::before - Hide the original via
position: absolute+clip - Add the
:focusstate
/* Hide together with radio — separated by a comma */
.radio,
.checkbox {
position: absolute;
width: 1px;
height: 1px;
clip-path: inset(50%);
overflow: hidden;
}
A modern alternative: accent-color
In file 06 we already mentioned accent-color — a property that lets you change the color of standard checkbox, radio, and range elements without custom styling:
input[type="radio"],
input[type="checkbox"] {
accent-color: #e53935;
}
- One line of CSS
- Preserves the standard behavior
- 100% accessibility
- Limited control (color only)
- Full control over the look
- The designer decides the shape and animation
- Requires complex CSS
- You have to take care of accessibility
accent-color. If full customization is needed — the mock-and-::before technique.Summary
| Topic | Key point |
|---|---|
display: inline-block |
Line + dimensions; centering via text-align; gap between elements |
<select> + <option> |
Dropdown list; selected — pre-selected; > 5 options |
appearance: none |
Resets the browser’s default styling for form elements |
background |
image + repeat + position + size; cover vs contain |
input type="radio" |
Choosing one from a group; name binds it into a radio group |
::before / ::after |
Decorative blocks via CSS; required content; not for input/img |
vertical-align |
Vertical alignment of inline/inline-block; middle for icons |
The + and ~ selectors |
Sibling selectors; + strict, ~ loose; only from older to younger |
| Custom styling of radio/checkbox | Mock via ::before + :checked state via ~ + accessible hiding |
position: absolute |
Pulls the element out of the flow; needed for clip |
clip-path: inset() |
Visual hiding while preserving accessibility; replaces the deprecated clip: rect() |
accent-color |
A modern alternative: change the color of radio/checkbox in one line |