Lesson 31 of 45 · Web pages

Styles and the box model

CSS decides how every element looks and how much room it takes. In Salam you write each CSS property as an attribute, with spaces instead of dashes, and the compiler checks it before the page is built. This lesson starts from nothing: how values are written, how the box model measures an element, and the properties that control size, space, display and overflow.

Style properties are attributes

A style property is written exactly like any other attribute: name = value inside the element. Property names have spaces where CSS has dashes, so max-width is max width and border-radius is border radius. A misspelled name is a compile error, and the message suggests the nearest real one.

The styles do not stay on the element. The compiler collects the styles of each element into one rule under a short generated class and puts the rules into the stylesheet. box: padding = 20 background = "#eef" end turns into <div class="a"> and the rule .a{padding:20px;background:#eef}. Elements whose styles are identical share one class, which keeps the page small.

You can also give an element a class of your own, but a class name must mean one set of styles. If two elements use the same class with different styles, the compiler stops with error E136 instead of letting one silently override the other. Give each its own class, drop the class and let the compiler name it, or style the shared class once with a global rule (shown below).

Values

Most values are one of four things. A bare number is pixels where a length is expected: padding = 24 is 24px, and 1.5 is 1.5px. Properties that take a plain number, such as line height and aspect ratio, keep it as a number. A string holds anything else: units, colors with #, several words. A keyword can be written without quotes: display = flex, overflow = hidden, color = white. And a function with simple arguments can be unquoted too, as in background = rgb(37, 99, 235).

Several lengths in one string form a shorthand. padding = "8 40" is 8px 40px: the first number is for top and bottom, the second for left and right. Up to four values are allowed, and a number without a unit gets px while a number with a unit keeps it, so "1rem 2" is 1rem 2px.

web-css-values.salam
page:
    title = "Style values"
    global: box sizing = "border-box" end
    global: selector = "body" font family = "system-ui, sans-serif" margin = 20 end
    global: selector = "p" margin = "0 0 10px" end
    paragraph: content = "padding = 24" padding = 24 background = "#e0f2fe" end
    paragraph: content = "padding = \"8 40\"" padding = "8 40" background = "#dcfce7" end
    paragraph: content = "border = \"3px dashed #0f766e\"" padding = 12 border = "3px dashed #0f766e" end
    paragraph: content = "width = \"60%\"" padding = 12 width = "60%" background = "#fef9c3" end
    paragraph:
        content = "background = \"linear-gradient(90deg, ...)\""
        padding = 12
        color = white
        background = "linear-gradient(90deg, #0f766e, #2563eb)"
    end
end

Values can also be computed while compiling: padding = GAP * 2 + "px" uses a constant (the section on reuse below shows how it is declared), and + joins a number and a unit into text.

Quote any value that has a #, a % or a unit inside a function, or a space-separated list. The compiler does not accept them bare, and calc() written without quotes is worse: it is accepted and silently becomes an empty calc(). Always write width = "calc(100% - 20px)".

Units

A length is a number and a unit. The ones you will use all the time:

  • px: a CSS pixel. Good for borders and fine details.
  • rem: the font size of the page, normally 16px. Sizes in rem grow when a reader raises the font size in the browser, so use it for text and spacing.
  • em: the font size of the element itself, which makes a padding follow the text size of its own box.
  • %: a share of the parent. For widths and horizontal padding and margin it is the parent's width, for a height it is the parent's height.
  • vw and vh: one percent of the width or height of the window. vmin and vmax take the smaller or larger of the two.
  • ch: the width of the digit 0 in the current font. max width = "65ch" keeps lines of text at a readable length.
  • cm, mm and in: real-world lengths, meant for printing.
web-css-units.salam
page:
    title = "Units"
    global: box sizing = "border-box" end
    global: selector = "body" font family = "system-ui, sans-serif" margin = 20 end
    global: selector = "p" margin = "0 0 8px" padding = "6px 10px" background = "#99f6e4" white space = nowrap end
    paragraph: content = "200px" width = "200px" end
    paragraph: content = "12rem" width = "12rem" end
    paragraph: content = "50%" width = "50%" end
    paragraph: content = "40vw" width = "40vw" end
    paragraph: content = "24ch" width = "24ch" end
    paragraph: content = "5cm" width = "5cm" end
end

The unit fr, a share of the free space in a grid, belongs to the layout lesson.

Colors

Wherever a color is expected you can write it in any CSS notation. The text color, the background and the fancy fills have their own lessons (backgrounds and colors); this is only the list of ways to write the value.

  • A name: teal, crimson, white.
  • Hex: "#0f766e", the short "#fa0", and with transparency "#0f766e80".
  • rgb(37, 99, 235): red, green and blue from 0 to 255. With transparency write "rgb(37 99 235 / 50%)".
  • hsl(160, 84%, 39%): hue as an angle on the color wheel, then saturation and lightness. Easy to adjust by hand.
  • oklch(0.7 0.15 30): lightness, chroma and hue in a space where equal steps look equal.
  • color-mix(in srgb, red 40%, white): a blend of two colors.
  • transparent and currentColor. The second is the element's own text color, so a border written with it follows the text.
web-css-colors.salam
page:
    title = "Colors"
    global: selector = "body" font family = "system-ui, sans-serif" margin = 20 end
    global: selector = "p" margin = "0 0 6px" padding = "8px 12px" end
    paragraph: content = "teal" background = teal color = white end
    paragraph: content = "#f59e0b" background = "#f59e0b" end
    paragraph: content = "rgb(37, 99, 235)" background = rgb(37, 99, 235) color = white end
    paragraph: content = "hsl(160, 84%, 39%)" background = "hsl(160, 84%, 39%)" end
    paragraph: content = "oklch(0.7 0.15 30)" background = "oklch(0.7 0.15 30)" end
    paragraph: content = "color-mix(in srgb, red 40%, white)" background = "color-mix(in srgb, red 40%, white)" end
    box:
        background = "#fde68a"
        padding = 6
        paragraph: content = "transparent shows what is behind" background = transparent end
    end
    paragraph: content = "currentColor makes the border follow the text" color = "#7c3aed" border = "3px solid currentColor" end
end

The box model

Every element is a rectangular box made of four layers, from the inside out. The content is the text or the children. Padding is the room between the content and the border, and it shows the element's background. The border is the line around the padding. The margin is empty space outside the border that keeps other boxes away. Padding and margin both take one to four values, in the order top, right, bottom, left, and each side also has its own property: padding top, margin left and so on.

web-css-box.salam
page:
    title = "The box model"
    global: selector = "body" font family = "system-ui, sans-serif" margin = 20 end
    box:
        background = "#e5e7eb"
        width = 320
        box:
            margin = 24
            border = "8px solid #1d4ed8"
            padding = 24
            background = "#bfdbfe"
            box:
                background = "white"
                padding = 8
                content = "content"
            end
        end
    end
    paragraph: content = "Grey: the space around the box (margin). Blue line: the border. Light blue: the padding. White: the content." end
end

The next question is what width = 240 measures. By default (content-box) it measures only the content, so a box with 20px of padding and a 6px border on each side is really 240 + 40 + 12 = 292 pixels wide, and a layout of several such boxes never adds up the way you wrote it. With box sizing = "border-box" the width includes padding and border, and 240 means 240. Click the button to see the same box measured both ways.

web-css-boxsizing.salam
import dom

page:
    title = "box sizing"
    global: selector = "body" font family = "system-ui, sans-serif" margin = 20 end
    global: selector = ".border" box sizing = "border-box" end
    box:
        id = "b"
        width = 240
        padding = 20
        border = "6px solid #0f766e"
        background = "#ccfbf1"
        content = "width = 240"
    end
    paragraph: id = "out" content = "The box is 292px wide" end
    button:
        content = "Switch box sizing"
        click = ():
            dom.ToggleClass(dom.ById("b"), "border")
            w := dom.GetNum(dom.ById("b"), "offsetWidth")
            dom.SetText(dom.ById("out"), "The box is now " + w + "px wide")
        end
    end
end

That is why nearly every page begins with the same line. A global element with no selector styles every element on the page:

global: box sizing = "border-box" end

Width, height and their limits

width and height set the size. By default a block is as wide as its parent and as tall as its content, and auto names that default. A percentage is a share of the parent, though a percentage height only works when the parent has a height of its own. Since a fixed size breaks when the window or the text changes, you will mostly set limits instead: min width and max width, min height and max height. A minimum beats a maximum, and a maximum beats the width. The sizes min-content, max-content and fit-content size a box by its text. aspect ratio keeps a box in shape ("16 / 9") when you give one side and leave the other free.

web-css-sizes.salam
page:
    title = "Sizes"
    global: box sizing = "border-box" end
    global: selector = "body" font family = "system-ui, sans-serif" margin = 20 end
    global: selector = "p" margin = "0 0 8px" padding = 10 background = "#e0e7ff" end
    paragraph: content = "min width 12rem, max width 24rem, width 50%" width = "50%" min width = "12rem" max width = "24rem" end
    paragraph: content = "width = clamp(10rem, 40%, 20rem)" width = "clamp(10rem, 40%, 20rem)" end
    paragraph: content = "height = 4rem" height = "4rem" end
    box:
        width = 200
        aspect ratio = "16 / 9"
        background = "#c7d2fe"
        padding = 10
        content = "aspect ratio = 16 / 9"
    end
end

A very common recipe is width = "100%" with max width = "40rem", optionally with margin = "0 auto" to center it: a box that fills a phone and stops growing on a monitor.

Logical properties

Left and right are physical directions. A page in English starts its lines on the left, a Persian page on the right, and a layout built from margin left has to be rewritten for the other language. Logical properties name the sides by the flow of the text. The inline direction is the one the text runs in and the block direction is the one the lines stack in. In English and Persian that makes inline horizontal and block vertical, and start is where a line begins: left in English, right in Persian.

So inline size is the width and block size the height, margin inline start and padding inline end are the sides at the start and the end of a line, and margin block and padding block are top and bottom. The example gives two boxes the very same styles and sets dir on the wrappers: the indent and the stripe change sides on their own.

web-css-logical.salam
page:
    title = "Logical properties"
    global: box sizing = "border-box" end
    global: selector = "body" font family = "system-ui, sans-serif" margin = 20 end
    global: selector = "p" margin = "0 0 12px" end
    box:
        dir = ltr
        paragraph:
            inline size = "70%"
            margin inline start = 40
            padding inline = "24 8"
            padding block = 10
            border inline start = "6px solid #0f766e"
            background = "#ccfbf1"
            content = "dir = ltr: the start side is the left"
        end
    end
    box:
        dir = rtl
        paragraph:
            inline size = "70%"
            margin inline start = 40
            padding inline = "24 8"
            padding block = 10
            border inline start = "6px solid #0f766e"
            background = "#ccfbf1"
            content = "dir = rtl: the same styles, mirrored"
        end
    end
end

For a site in both languages, logical properties are usually the better choice. Use left and right when you really mean the screen: a shadow that must fall to the right, for instance.

Margins in practice

Four rules about margins explain most surprises.

  • Centering. A block with a width and margin = "0 auto" is centered: auto splits the free space between the two sides. A block without a width fills the parent, so there is nothing to split.
  • Collapsing. When two blocks are stacked, the margin between them is not the sum of the two but the larger one. A margin bottom = 40 above a margin top = 20 leaves 40. The same happens between a parent and its first or last child when nothing (padding, border, a height) separates them. Margins inside a flex or grid layout never collapse.
  • Negative margins. A margin may be negative, which pulls the element toward its neighbour or out of its parent. Use it sparingly; it hides the real structure.
  • Which one to use. Padding when the space should be part of the box and show its background, margin when it should push other boxes away.
web-css-margins.salam
page:
    title = "Margins"
    global: box sizing = "border-box" end
    global: selector = "body" font family = "system-ui, sans-serif" margin = 20 end
    global: selector = "p" margin = 0 padding = 10 background = "#fed7aa" end
    paragraph: content = "width = 240, margin = \"0 auto 30px\"" width = 240 margin = "0 auto 30px" end
    paragraph: content = "margin bottom = 40" margin bottom = 40 end
    paragraph: content = "margin top = 20 (the gap above is 40, not 60)" margin top = 20 background = "#fdba74" end
    paragraph: content = "margin top = -12 pulls this one up over the last" margin top = -12 margin left = 40 background = "#fb923c" end
end

Display and visibility

display decides how an element takes part in the layout. block starts a new line and fills the width, inline sits inside a line of text and ignores width and height, and inline-block sits in a line but obeys them. flex and grid turn the element into a container that lays out its children, which is the whole topic of the layout lesson. contents removes the element's own box and lets its children take part in the parent's layout as if it were not there, and flow-root makes a box that contains everything inside it, floats included. Every element starts with a default: a box is block and a span is inline.

web-css-display.salam
page:
    title = "display"
    global: box sizing = "border-box" end
    global: selector = "body" font family = "system-ui, sans-serif" margin = 20 end
    global: selector = "span" width = 90 height = 40 padding = 6 margin = 2 background = "#bbf7d0" border = "1px solid #15803d" end
    heading: size = 3 content = "inline (the default for span)" end
    box:
        span: content = "one" end
        span: content = "two" end
        span: content = "three" end
    end
    heading: size = 3 content = "inline-block" end
    box:
        span: display = inline-block content = "one" end
        span: display = inline-block content = "two" end
        span: display = inline-block content = "three" end
    end
    heading: size = 3 content = "block" end
    box:
        span: display = block content = "one" end
        span: display = block content = "two" end
    end
end

To hide an element you have two choices that behave differently. display = none takes it out of the page completely: it has no box and the following elements move up. visibility = hidden only stops it being drawn: the gap stays. In both cases the element cannot be clicked, cannot receive keyboard focus and is skipped by screen readers. The two buttons below set the style from a script, so you can watch the paragraph that follows.

web-css-hiding.salam
import dom

page:
    title = "Hiding"
    global: selector = "body" font family = "system-ui, sans-serif" margin = 20 end
    button:
        content = "display: none"
        click = ():
            if dom.GetStyle(dom.ById("target"), "display") == "none":
                dom.SetStyle(dom.ById("target"), "display", "")
            else:
                dom.SetStyle(dom.ById("target"), "display", "none")
            end
        end
    end
    button:
        content = "visibility: hidden"
        click = ():
            if dom.GetStyle(dom.ById("target"), "visibility") == "hidden":
                dom.SetStyle(dom.ById("target"), "visibility", "")
            else:
                dom.SetStyle(dom.ById("target"), "visibility", "hidden")
            end
        end
    end
    paragraph: content = "Before" background = "#e5e7eb" end
    paragraph: id = "target" content = "The target" background = "#fca5a5" end
    paragraph: content = "After" background = "#e5e7eb" end
end

Overflow

What happens when the content is bigger than its box is the job of overflow. With visible, the default, it spills out and covers what is next to it. hidden cuts it at the border, scroll always shows scrollbars, and auto shows them only when there is something to scroll, which is nearly always what you want. clip cuts like hidden but never allows scrolling, even from a script, and it is the only value that works with overflow clip margin. overflow x and overflow y set one direction at a time, and overflow block and overflow inline are their logical forms, which fewer browsers know today.

web-css-overflow.salam
page:
    title = "overflow"
    global: box sizing = "border-box" end
    global: selector = "body" font family = "system-ui, sans-serif" margin = 20 end
    box:
        display = flex
        flex wrap = wrap
        gap = "40 24"
        box: width = 150 height = 90 padding = 8 border = "2px solid #6366f1" font size = 14 overflow = visible content = "visible: A text that is longer than the box it sits in, so it must go somewhere." end
        box: width = 150 height = 90 padding = 8 border = "2px solid #6366f1" font size = 14 overflow = hidden content = "hidden: A text that is longer than the box it sits in, so it must go somewhere." end
        box: width = 150 height = 90 padding = 8 border = "2px solid #6366f1" font size = 14 overflow = auto content = "auto: A text that is longer than the box it sits in, so it must go somewhere." end
        box: width = 150 height = 90 padding = 8 border = "2px solid #6366f1" font size = 14 overflow = scroll content = "scroll: A text that is longer than the box it sits in, so it must go somewhere." end
    end
end

A box with a fixed height and a lot of text is the usual cause of text running over the next paragraph. Prefer min height, or give the box an overflow value on purpose. The values hidden, scroll and auto also stop a child's margin from escaping through the box, and make the box contain its floats.

calc, min, max, clamp and var

CSS can do arithmetic where a length goes. calc() mixes units: "calc(100% - 80px)" is everything but 80 pixels, and the spaces around + and - are required. min() picks the smallest of its arguments and max() the largest, so "min(100%, 20rem)" says "at most 20rem". clamp(least, ideal, most) is the pair in one: "clamp(1rem, 4vw, 2rem)" is a font size that scales with the window but stays between 1 and 2 rem.

var() reads a CSS custom property, a value you name yourself. They are declared in raw CSS, which you give to a style element at the top of the page. The name starts with two dashes, and it can be used anywhere below through "var(--brand)".

web-css-functions.salam
page:
    title = "CSS functions"
    style: content = ":root { --brand: #0f766e; --gap: 14px }" end
    global: box sizing = "border-box" end
    global: selector = "body" font family = "system-ui, sans-serif" margin = 20 end
    global: selector = "p" margin = "0 0 10px" padding = "var(--gap)" end
    paragraph: content = "width = calc(100% - 80px)" width = "calc(100% - 80px)" background = "#e0f2fe" end
    paragraph: content = "width = min(100%, 20rem)" width = "min(100%, 20rem)" background = "#dcfce7" end
    paragraph: content = "width = max(50%, 18rem)" width = "max(50%, 18rem)" background = "#fef9c3" end
    paragraph: content = "font size = clamp(1rem, 4vw, 2rem)" font size = "clamp(1rem, 4vw, 2rem)" background = "#fce7f3" end
    paragraph: content = "background = var(--brand)" background = "var(--brand)" color = white end
end

The difference from the constants of Salam is when the value is known. A constant is fixed while compiling. A custom property lives in the browser, so it can change with the window size, with a dark mode rule, or from a script.

Global rules

Styles on an element are the right tool for that element. The global element is for everything else: a rule for a whole kind of element, or for the page. Its selector is any CSS selector, written as text. A tag name styles every element of that kind, ".tip" every element with that class, "#lead" one element with that id, and "li:nth-child(odd)" or "nav a" pick by position or place. Leave the selector out and the rule applies to every element, which is how the box sizing line above works.

web-css-global.salam
page:
    title = "Global rules"
    global: box sizing = "border-box" end
    global: selector = "body" font family = "system-ui, sans-serif" margin = 20 line height = 1.5 end
    global: selector = "h2" color = "#0f766e" margin = "0 0 8px" end
    global: selector = ".tip" background = "#fef3c7" border left = "4px solid #f59e0b" padding = "8px 12px" end
    global: selector = "#lead" font size = 20 end
    global: selector = "li:nth-child(odd)" background = "#f1f5f9" end
    heading: size = 2 content = "Three kinds of selector" end
    paragraph: id = "lead" content = "A tag, a class and an id, all styled once." end
    paragraph: class = "tip" content = "Every element with class tip gets this look." end
    list:
        item: content = "Odd rows are shaded" end
        item: content = "Even rows are not" end
        item: content = "Odd again" end
    end
end

Two details to know. The selector is passed to the stylesheet as written, so the compiler cannot tell you that it is misspelled. And when two rules reach the same element the more specific one wins: a class beats a tag, an id beats a class. Use global for defaults and shared classes, element styles for what is unique.

Constants and components

Write a number once and use it everywhere. A const above the page holds a value that is worked out while compiling, and style values may use it in expressions: padding = GAP * 2 + "px". A component does the same for a whole piece of page: it takes parameters, builds a styled element tree, and content: end marks where the children of each use go. Parameters work in styles too: a value such as overflow = mode reads the parameter mode. Uses with identical styles share one generated class, so a card used ten times adds one rule.

web-css-components.salam
const GAP := 16
const BRAND := "#0f766e"

component Card(title, body, accent):
    box:
        padding = GAP
        margin = "0 0 " + GAP + "px"
        border = "2px solid " + accent
        border radius = GAP / 2 + "px"
        heading: size = 3 content = title color = accent margin = "0 0 6px" end
        paragraph: content = body margin = 0 end
        content: end
    end
end

page:
    title = "Reusable cards"
    global: selector = "body" font family = "system-ui, sans-serif" margin = GAP * 2 + "px" end
    Card: title = "Fast" body = "Built to machine code." accent = BRAND end
    Card: title = "Safe" body = "Checked while compiling." accent = "#2563eb"
        paragraph: content = "Even the page styles." color = "#64748b" end
    end
end

Skipping work for big pages

Three properties help a long page load and scroll faster, at the price of some promises. contain tells the browser that the inside of an element does not affect anything outside it, so a change inside never needs the whole page to be laid out again. content visibility = auto goes further: the browser does not draw or lay out the element while it is far off screen. Because a skipped element has no height, add contain intrinsic size with a guess, auto 600px meaning "use the real size once you have seen it, and 600px before". Use it on the sections of a page that is long, not on small pages, where it only adds risk.

section:
    content visibility = auto
    contain intrinsic size = "auto 600px"
    heading: size = 2 content = "Far down the page" end
    paragraph: content = "Drawn only when the reader scrolls near it." end
end

Reset and field sizing

all resets every property of an element at once. Only the keywords that work for any property are allowed: initial, inherit, unset and revert. Put it first and then add back the styles you want, the way the reference example turns a button into a plain box. field sizing = content lets a text box grow while the user types, with no script, in browsers that support it. The properties appearance, resize and zoom are in the responsive lesson.

web-css-fieldsizing.salam
page:
    title = "field sizing"
    global: selector = "body" font family = "system-ui, sans-serif" margin = 20 end
    global: selector = "p" margin = "0 0 8px" end
    paragraph: content = "Type in the box: it grows with the text." end
    input:
        id = "name"
        field sizing = content
        min width = "6ch"
        max width = "100%"
        padding = 6
        font size = 18
        placeholder = "Your name"
    end
    paragraph: content = "Without it, a text box keeps one fixed width:" margin top = 24 end
    input: padding = 6 font size = 18 placeholder = "Your name" end
end

Reference: sizing, space, display and overflow

Every property of this lesson is below with what it accepts and a short example that the compiler has checked. Persian names work in any file and are what the Persian lessons use. First the global element, then the properties in groups.

The global rule

global (Persian: سراسری)

Holds a style rule for the whole page instead of for one element. With no selector the rule applies to every element, which is the place for resets such as box sizing. With a selector it applies to everything the selector matches. It takes any style property, with the hover, focus and similar prefixes too.

global: selector = ".tip" background = "#fef3c7" padding = 12 hover background = "#fde68a" end
AttributeWhat it doesExample
selector
گزینشگر
A CSS selector as text: a tag such as "h2", a class ".card", an id "#intro", a state "a:hover", a position "li:nth-child(odd)" or a combination "nav a". The text is passed to the stylesheet as it is, so the compiler does not check it. [string]global: selector = "li:nth-child(odd)" background = "#f1f5f9" end

Width, height and shape

Each size has a physical form (width, height) and a logical form (inline size, block size), and a minimum and a maximum of each.

PropertyWhat it does and what it acceptsExample
width
عرض
width
The width of the element. A number is pixels. auto (the default) lets a block fill its parent, a percentage is a share of the parent's width, and min-content, max-content and fit-content size the box by its text. With the default box sizing it is the width of the content only. [length / percentage / auto, min-content, max-content, fit-content]box: width = "60%" background = "#bfdbfe" padding = 8 content = "60% of the parent" end
min width
کمینه عرض
min-width
The smallest width the element may have. It beats both width and max width. Use it to keep a column readable when the window shrinks. [length / percentage / auto, min-content, max-content, fit-content]box: width = "20%" min width = 160 background = "#bfdbfe" padding = 8 content = "never under 160px" end
max width
بیشینه عرض
max-width
The largest width the element may have; none (the default) means no limit. The classic pair is width = "100%" with max width = "40rem": full width on a phone, a comfortable line length on a monitor. [length / percentage / none, min-content, max-content, fit-content]box: max width = "24rem" background = "#bfdbfe" padding = 8 content = "As wide as the parent allows, but never over 24rem." end
height
ارتفاع
height
The height of the element. auto (the default) makes the box as tall as its content. A percentage works only when the parent has a height of its own. A fixed height with more content inside needs an overflow choice. [length / percentage / auto, min-content, max-content, fit-content]box: height = 80 background = "#bbf7d0" padding = 8 content = "80px tall" end
min height
کمینه ارتفاع
min-height
The smallest height the element may have. Unlike a fixed height it lets the box grow when the content needs more room, which makes it the safer choice for cards and sections. [length / percentage / auto, min-content, max-content, fit-content]box: min height = 100 background = "#bbf7d0" padding = 8 content = "At least 100px, taller if needed" end
max height
بیشینه ارتفاع
max-height
The largest height the element may have; none means no limit. Combine it with overflow = auto to get a box that scrolls once its content is taller than the limit. [length / percentage / none, min-content, max-content, fit-content]box: max height = 60 overflow = auto background = "#bbf7d0" padding = 8 content = "A long text that does not fit in sixty pixels, so the box gets a scrollbar instead of growing taller and taller." end
inline size
اندازه خطی
inline-size
The size along the direction text runs: the width in English and Persian pages, the height in vertical writing modes. It is the logical form of width, so a layout written with it keeps working when the writing mode changes. Values are the same as for width. [length / percentage / auto, min-content, max-content, fit-content]box: inline size = "50%" background = "#e9d5ff" padding = 8 content = "inline size = 50%" end
min inline size
کمینه اندازه خطی
min-inline-size
The logical min width: the smallest size along the text direction. [length / percentage / auto, min-content, max-content, fit-content]box: inline size = "20%" min inline size = 160 background = "#e9d5ff" padding = 8 content = "never under 160px" end
max inline size
بیشینه اندازه خطی
max-inline-size
The logical max width: the largest size along the text direction. max inline size = "60ch" keeps lines of text near sixty characters in any language and direction. [length / percentage / none, min-content, max-content, fit-content]box: max inline size = "30ch" background = "#e9d5ff" padding = 8 content = "A line of text never gets longer than thirty characters in this box." end
block size
اندازه بلوکی
block-size
The size across the direction text runs: the height in English and Persian pages. It is the logical form of height. Values are the same as for height. [length / percentage / auto, min-content, max-content, fit-content]box: block size = 70 background = "#e9d5ff" padding = 8 content = "block size = 70" end
min block size
کمینه اندازه بلوکی
min-block-size
The logical min height: the smallest size across the text direction. [length / percentage / auto, min-content, max-content, fit-content]box: min block size = 90 background = "#e9d5ff" padding = 8 content = "at least 90px" end
max block size
بیشینه اندازه بلوکی
max-block-size
The logical max height: the largest size across the text direction. Pair it with overflow for a scrolling box. [length / percentage / none, min-content, max-content, fit-content]box: max block size = 50 overflow = auto background = "#e9d5ff" padding = 8 content = "A long text that needs more than fifty pixels of height, so it scrolls." end
aspect ratio
نسبت ابعاد
aspect-ratio
Keeps the box in a fixed shape: the width divided by the height, written "16 / 9" or as one number such as 1.5. It takes effect when one of the two sizes is auto, so give a width and let the height follow. auto uses the natural ratio of an image or video when it has one. [ratio / auto]box: width = 240 aspect ratio = "16 / 9" background = "#c7d2fe" content = "16 / 9" end
box sizing
اندازه جعبه
box-sizing
What width and height measure. content-box (the default) is the content alone, so padding and border are added on top. border-box includes padding and border, so the box is exactly the size you wrote. Most pages set border-box once for every element with a global rule. [content-box, border-box]box: box sizing = border-box width = 200 padding = 20 border = "4px solid teal" content = "Exactly 200px wide" end

Margin

The space outside the border. margin sets all sides, the top, right, bottom and left forms set one physical side, and the block and inline forms follow the text direction.

PropertyWhat it does and what it acceptsExample
margin
فضا
margin
The space outside the border. One value is for all four sides, two are vertical then horizontal ("10 20"), three are top, horizontal, bottom, and four go clockwise from the top. auto shares out the free space, so "0 auto" centers a box that has a width. Negative values are allowed. A percentage is a share of the parent's width. [length / percentage / auto; up to 4 values]box: margin = "10 40" background = "#fed7aa" content = "margin = 10 40" end
margin top
فضا بالا
margin-top
The space above the box. Vertical margins of neighbouring blocks collapse into the larger of the two instead of adding up. [length / percentage / auto]box: margin top = 30 background = "#fed7aa" content = "30px of space above" end
margin right
فضا راست
margin-right
The space on the right of the box, whatever the text direction. margin left = auto pushes a box to the right edge. Prefer margin inline end when the side should follow the language. [length / percentage / auto]box: width = 200 margin left = auto background = "#fed7aa" content = "pushed to the right" end
margin bottom
فضا پایین
margin-bottom
The space below the box. Vertical margins of neighbouring blocks collapse into the larger of the two. [length / percentage / auto]box: margin bottom = 30 background = "#fed7aa" content = "30px of space below" end
margin left
فضا چپ
margin-left
The space on the left of the box, whatever the text direction. margin right = auto pushes a box to the left edge, and margin left with margin right both auto centers it. [length / percentage / auto]box: width = 200 margin left = 60 background = "#fed7aa" content = "60px from the left" end
margin block
فضا بلوکی
margin-block
The margin at the start and end of the block direction, which is top and bottom in English and Persian pages. One value sets both, two values are start then end. [length / percentage / auto; up to 2 values]box: margin block = "20 40" background = "#fed7aa" content = "20 before, 40 after" end
margin block start
فضا بلوکی ابتدا
margin-block-start
The margin before the box in the block direction: the top in English and Persian pages. [length / percentage / auto]box: margin block start = 24 background = "#fed7aa" content = "24px before" end
margin block end
فضا بلوکی انتها
margin-block-end
The margin after the box in the block direction: the bottom in English and Persian pages. [length / percentage / auto]box: margin block end = 24 background = "#fed7aa" content = "24px after" end
margin inline
فضا خطی
margin-inline
The margin at the start and end of the text direction, which is left and right in English and right and left in Persian. One value sets both, two values are start then end. [length / percentage / auto; up to 2 values]box: margin inline = "10 60" background = "#fed7aa" content = "10 at the start, 60 at the end" end
margin inline start
فضا ابتدا
margin-inline-start
The margin on the side where text starts: the left in English, the right in Persian. It is the side to use for indents, because it flips with the language. [length / percentage / auto]box: margin inline start = 40 background = "#fed7aa" content = "indented from the start side" end
margin inline end
فضا انتها
margin-inline-end
The margin on the side where text ends: the right in English, the left in Persian. [length / percentage / auto]box: margin inline end = 40 background = "#fed7aa" content = "space kept at the end side" end

Padding

The space inside the border. It has the same set of sides as margin, and it never takes auto or a negative value.

PropertyWhat it does and what it acceptsExample
padding
فاصله
padding
The space between the content and the border, inside the box. It shows the element's background. One value is for all four sides, two are vertical then horizontal, three are top, horizontal, bottom, and four go clockwise from the top. It cannot be negative or auto. [length / percentage; up to 4 values]box: padding = "8 24" background = "#bbf7d0" content = "padding = 8 24" end
padding top
فاصله بالا
padding-top
The padding above the content. [length / percentage]box: padding top = 30 background = "#bbf7d0" content = "30px of padding above" end
padding right
فاصله راست
padding-right
The padding on the right of the content, whatever the text direction. [length / percentage]box: padding right = 60 background = "#bbf7d0" content = "60px of padding on the right" end
padding bottom
فاصله پایین
padding-bottom
The padding below the content. [length / percentage]box: padding bottom = 30 background = "#bbf7d0" content = "30px of padding below" end
padding left
فاصله چپ
padding-left
The padding on the left of the content, whatever the text direction. [length / percentage]box: padding left = 60 background = "#bbf7d0" content = "60px of padding on the left" end
padding block
فاصله بلوکی
padding-block
The padding at the start and end of the block direction, which is top and bottom in English and Persian pages. One value sets both, two values are start then end. [length / percentage; up to 2 values]box: padding block = "4 20" background = "#bbf7d0" content = "4 before, 20 after" end
padding block start
فاصله بلوکی ابتدا
padding-block-start
The padding before the content in the block direction: the top in English and Persian pages. [length / percentage]box: padding block start = 24 background = "#bbf7d0" content = "24px before" end
padding block end
فاصله بلوکی انتها
padding-block-end
The padding after the content in the block direction: the bottom in English and Persian pages. [length / percentage]box: padding block end = 24 background = "#bbf7d0" content = "24px after" end
padding inline
فاصله خطی
padding-inline
The padding at the start and end of the text direction, which is left and right in English and right and left in Persian. One value sets both, two values are start then end. [length / percentage; up to 2 values]box: padding inline = "30 10" background = "#bbf7d0" content = "30 at the start, 10 at the end" end
padding inline start
فاصله ابتدا
padding-inline-start
The padding on the side where text starts: the left in English, the right in Persian. [length / percentage]box: padding inline start = 40 background = "#bbf7d0" content = "40px of padding at the start side" end
padding inline end
فاصله انتها
padding-inline-end
The padding on the side where text ends: the right in English, the left in Persian. [length / percentage]box: padding inline end = 40 background = "#bbf7d0" content = "40px of padding at the end side" end

Overflow

What happens to content that does not fit.

PropertyWhat it does and what it acceptsExample
overflow
سرریز
overflow
What happens to content that is bigger than its box. visible (the default) lets it spill out, hidden cuts it off, clip cuts it off with no way to scroll at all, scroll always shows scrollbars and auto shows them only when needed. One value sets both directions, two values are horizontal then vertical. [visible, hidden, clip, scroll, auto; up to 2 values]box: width = 160 height = 50 overflow = auto border = "1px solid #94a3b8" content = "A text that is longer than the small box it sits in, so it has to go somewhere." end
overflow x
سرریز افقی
overflow-x
What happens to content that is too wide for the box. Values are the same as for overflow. If you set one axis to something other than visible, a visible on the other axis becomes auto. [visible, hidden, clip, scroll, auto]box: width = 160 overflow x = scroll white space = nowrap border = "1px solid #94a3b8" content = "One long line that does not wrap and runs past the right edge." end
overflow y
سرریز عمودی
overflow-y
What happens to content that is too tall for the box. Values are the same as for overflow. auto is the usual choice for a panel with a fixed height. [visible, hidden, clip, scroll, auto]box: height = 50 width = 160 overflow y = auto border = "1px solid #94a3b8" content = "A text that is longer than the small box it sits in, so it has to go somewhere." end
overflow block
سرریز بلوکی
overflow-block
The overflow along the block direction (vertical in English and Persian pages). It is the logical form of overflow y. Only some browsers support it yet, so overflow y is the safer choice today. [visible, hidden, clip, scroll, auto]box: block size = 50 inline size = 160 overflow block = auto border = "1px solid #94a3b8" content = "A text that is longer than the small box it sits in, so it has to go somewhere." end
overflow inline
سرریز خطی
overflow-inline
The overflow along the text direction (horizontal in English and Persian pages). It is the logical form of overflow x. Only some browsers support it yet, so overflow x is the safer choice today. [visible, hidden, clip, scroll, auto]box: inline size = 160 overflow inline = scroll white space = nowrap border = "1px solid #94a3b8" content = "One long line that does not wrap and runs past the end." end
overflow anchor
لنگر سرریز
overflow-anchor
Turns scroll anchoring on (auto, the default) or off (none). Anchoring is the browser's trick of keeping what you are reading in place when content above it loads or changes size. Turn it off only for a scroller whose own script manages the scroll position. [CSS-wide keywords only]box: height = 60 overflow = auto overflow anchor = none border = "1px solid #94a3b8" content = "A text that is longer than the small box it sits in, so it has to go somewhere." end
overflow clip margin
فضا برش سرریز
overflow-clip-margin
With overflow = clip, how far past the box edge the content may still be painted before it is cut: a length such as "12px", optionally after content-box, padding-box or border-box to say which edge to measure from. It does nothing with the other overflow values. [CSS-wide keywords only]box: overflow = clip overflow clip margin = "12px" border = "2px solid #6366f1" padding = 8 width = 140 height = 40 content = "A text that is longer than the small box it sits in, so it has to go somewhere." end

Display and containment

How the element takes part in the layout, whether it is drawn, and what the browser may skip.

PropertyWhat it does and what it acceptsExample
display
نمایش
display
How the element takes part in the layout. block starts a new line and fills the width, inline flows inside a line of text and ignores width and height, inline-block flows like text but accepts width and height, none removes the element from the page, flex and grid lay the children out in rows or tracks, flow-root makes a box that contains its floats, and contents removes the box but keeps the children. The elements have their own default: span is inline, box is block. [block, inline, inline-block, flex, inline-flex, grid, ...]box: display = flex gap = 8 span: content = "one" background = "#bbf7d0" padding = 8 end span: content = "two" background = "#bbf7d0" padding = 8 end end
visibility
دیدپذیری
visibility
Hides an element but keeps its place: hidden leaves an empty gap, visible is the default. Unlike display = none the space is still used. A hidden element cannot be clicked or focused, and screen readers skip it. collapse removes a table row or column and frees its space, and acts like hidden elsewhere. [visible, hidden, collapse]box: visibility = hidden background = "#fca5a5" padding = 8 content = "You cannot see me, but my space is kept" end
contain
محدودسازی
contain
Promises the browser that what is inside the element does not affect the rest of the page, so it can skip work. layout, paint, size, inline-size and style are the single promises; content means layout, paint and style; strict adds size to those; none promises nothing. With size the box no longer grows with its content, so give it a size. [none, strict, content, size, inline-size, layout, ...; up to 4 values]box: contain = content background = "#fef9c3" padding = 8 content = "A widget whose inside never changes the layout outside" end
content visibility
دیدپذیری محتوا
content-visibility
Lets the browser skip the work for parts of the page that are not on screen. auto skips an element until it is near the visible area, hidden always skips it but keeps its rendered state (cheaper to show again than display = none), and visible is the default. Pair auto with contain intrinsic size so the scroll bar does not jump. [visible, auto, hidden]box: content visibility = auto contain intrinsic size = "auto 120px" background = "#fef9c3" padding = 8 content = "A long section far down the page" end
contain intrinsic size
اندازه ذاتی محدود
contain-intrinsic-size
The size the browser should assume for an element whose content is being skipped (by content visibility) or contained by size. One or two lengths, width then height. An auto in front, as in "auto 300px", means: use the last size it really had, and 300px until then. [length; up to 4 values]box: content visibility = auto contain intrinsic size = "auto 300px 200px" background = "#fef9c3" padding = 8 content = "placeholder 300 wide, 200 tall" end
contain intrinsic width
عرض ذاتی محدودسازی
contain-intrinsic-width
Only the width part of contain intrinsic size: the width assumed for a skipped or size-contained element, optionally with auto in front. [length; up to 2 values]box: content visibility = auto contain intrinsic width = "auto 300px" background = "#fef9c3" padding = 8 content = "assumed 300px wide" end
contain intrinsic height
ارتفاع ذاتی محدودسازی
contain-intrinsic-height
Only the height part of contain intrinsic size: the height assumed for a skipped or size-contained element, optionally with auto in front. [length; up to 2 values]box: content visibility = auto contain intrinsic height = "auto 200px" background = "#fef9c3" padding = 8 content = "assumed 200px tall" end
contain intrinsic inline size
اندازه ذاتی محدودسازی خطی
contain-intrinsic-inline-size
The logical form of contain intrinsic width: the size along the text direction assumed for a skipped or size-contained element. [length; up to 2 values]box: content visibility = auto contain intrinsic inline size = "auto 300px" background = "#fef9c3" padding = 8 content = "assumed 300px along the text" end
contain intrinsic block size
اندازه ذاتی محدودسازی بلوکی
contain-intrinsic-block-size
The logical form of contain intrinsic height: the size across the text direction assumed for a skipped or size-contained element. [length; up to 2 values]box: content visibility = auto contain intrinsic block size = "auto 200px" background = "#fef9c3" padding = 8 content = "assumed 200px across the text" end

Reset and field size

PropertyWhat it does and what it acceptsExample
all
همه ویژگی‌ها
all
Resets every style property of the element at once. Only the CSS-wide keywords are allowed: initial (the property's own default), inherit, unset (inherit if the property normally inherits, otherwise initial) and revert (the browser's stylesheet). Write it first, then add the styles you want back. It leaves direction alone. [CSS-wide keywords only]button: all = unset padding = "8 16" background = "#0f766e" color = white cursor = pointer content = "A button with no browser look" end
field sizing
اندازه فیلد
field-sizing
Lets a text box, text area or select size itself to what it contains. content makes it grow and shrink as the user types, fixed (the default) keeps the usual fixed size. Browsers that do not know it keep the fixed size, so the page still works. [fixed / content]input: field sizing = content min width = "8ch" padding = 6 placeholder = "Type here" end

Try it yourself

  • Build a card with padding = 24, a border and max width = "24rem". Put five of them in a column and add margin = "0 auto 16px". Is the gap between two cards 16px or 32px?
  • Take the box example and write global: box sizing = "border-box" end at the top. Which sizes change, and why does the box look smaller?
  • Make a 150 by 100 box with a long sentence in it and try each value of overflow. Then replace height = 100 with min height = 100.
  • Replace every margin left and margin right of a page with the logical forms, then add dir = rtl to the page and look at the result.