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
- Values
- Units
- Colors
- The box model
- Width, height and their limits
- Logical properties
- Margins in practice
- Display and visibility
- Overflow
- calc, min, max, clamp and var
- Global rules
- Constants and components
- Skipping work for big pages
- Reset and field sizing
- Reference: sizing, space, display and overflow
- Try it yourself
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.
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
endValues 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 inremgrow 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.vwandvh: one percent of the width or height of the window.vminandvmaxtake 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,mmandin: real-world lengths, meant for printing.
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
endThe 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.transparentandcurrentColor. The second is the element's own text color, so a border written with it follows the text.
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
endThe 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.
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
endThe 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.
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
endThat 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.
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
endA 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.
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
endFor 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:autosplits 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 = 40above amargin top = 20leaves 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.
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
endDisplay 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.
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
endTo 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.
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
endOverflow
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.
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
endA 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)".
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
endThe 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.
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
endTwo 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.
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
endSkipping 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.
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
endReference: 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
| Attribute | What it does | Example |
|---|---|---|
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.
| Property | What it does and what it accepts | Example |
|---|---|---|
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.
| Property | What it does and what it accepts | Example |
|---|---|---|
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.
| Property | What it does and what it accepts | Example |
|---|---|---|
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.
| Property | What it does and what it accepts | Example |
|---|---|---|
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.
| Property | What it does and what it accepts | Example |
|---|---|---|
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
| Property | What it does and what it accepts | Example |
|---|---|---|
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 andmax width = "24rem". Put five of them in a column and addmargin = "0 auto 16px". Is the gap between two cards 16px or 32px? - Take the box example and write
global: box sizing = "border-box" endat 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 replaceheight = 100withmin height = 100. - Replace every
margin leftandmargin rightof a page with the logical forms, then adddir = rtlto the page and look at the result.