Debugging CSS
-
A fundamental concept for CSS layout is inline vs. block elements. Inline means elements only take up the space they need, and do not affect flow. Applying
margin
orpadding
to an inline element will only work in the “inline” direction (left/right) not the “block” direction (top/bottom). -
Sometimes when we include an image in our markup, a bit of mysterious space is added underneath. The problem is that images by default are considered inline elements, and setting
line-height: 0
on the parent can solve it. Another way is to set the imagedisplay: block
, which tells flow layout: Hey, this image isn’t a word in a sentence. It’s a block element, and block elements never have any of this inline magic space. The third way to solve this is not using flow layout, but set parentdisplay: flex
. -
input
,video
,img
,iframe
,embed
are replaced elements whose width and height are predefined, without CSS.iframe
has the default width300px
and height150px
. -
When an element has a
position
value ofabsolute
, it becomes a block-level element by default. This means that addinginline-block
orblock
as the display type won’t affect it at all. -
The
<p>
element represents a paragraph. It cannot contain block-level elements including<p>
itself. For example,<p><div>hello</div></p>
will be parsed as<p></p><div>hello</div><p></p>
in Chrome. (Invalid HTML will be fixed by the browser) -
When you apply a
float
to an element with a display type offlex
orinline-flex
, it won’t affect the element at all. -
Say you have two elements, the one above with
margin-bottom
, and the one below withmargin-top
. The greater of the two values will be used as the margin between the elements, and the other will be ignored by the browser. -
margin: auto
is a popular way to center an element, and it’s important to mention that auto margins (e.g.margin-right: auto
) will take up the extra space and apply it to the element’s margin. -
You can’t set a percentage-based height for an element unless the height of its parent is explicitly defined. You can use
body { height: 100vh }
to make thebody
element take up the full height of the viewport. -
Elements are grouped into stacking contexts. When we give an element a
z-index
, that value is only compared against other elements in the same context.z-index
values are not global. By default, a plain HTML document will have a single stacking context that encompasses all nodes. But there are many ways to create stacking contexts, e.g., combining relative or absolute positioning withz-index
; Setting position tofixed
orsticky
; Settingopacity
to a value less than 1; Adding az-index
to a child inside adisplay: flex
ordisplay: grid
container; Usingtransform
,filter
,perspective
. -
The
::before
pseudo-element becomes the first child of its parent, whereas::after
is added as the last child. The default display value of a pseudo-element isinline
. So when you add a width, height, vertical padding or vertical margin, it won’t work unless thedisplay
type is changed. -
By default, the
color
property is inherited by child elements such asp
andspan
. Instead of setting the color property on each element, add it to thebody
, and then allp
andspan
elements will inherit that color. However, thea
,input
, orbutton
element doesn’t inheritcolor
by default. You can override its color or use theinherit
keyword (to inherit those non-inheritable properties.) -
A common mistake when showing a border on hover is to add the border only on hover. If the border is 1 pixel, then the element will jump by that much when the user hovers over it. To avoid the jump, add the border to the normal state with a transparent color.
-
Unlike border,
outline
is drawn outside the element’s border and may overlap other content. Also, the outline is not a part of the element’s dimensions; the element’s total width and height is not affected by the width of the outline. You can override it with a custom one, but don’t remove that outline under any circumstances, because it will affect the accessibility of the website./* debug your CSS layouts with one line */ * { outline: 1px solid #f00 !important; }
-
Find element that is causing the showing of horizontal scrollbar.
let all = document.getElementsByTagName("*"), rect, docWidth = document.documentElement.offsetWidth; for (let i = 0; i < all.length; i++) { rect = all[i].getBoundingClientRect(); if (rect.right > docWidth || rect.left < 0){ console.log(all[i]); all[i].style.outline = '1px solid red'; } }
getBoundingClientRect
returns aDOMRect
object which is the smallest rectangle which contains the entire element, including its padding and border-width. The left, top, right, bottom, x, y, width, and height properties describe the position and size of the overall rectangle in pixels. Properties other than width and height are relative to the top-left of the viewport. -
A long word or link can easily cause horizontal overflow (scrolling). The solution is to use
overflow-wrap: break-word
. It’s worth mentioning that the property has been renamed fromword-wrap
tooverflow-wrap
. -
Flexbox doesn’t wrap by default, thus may cause horizontal scrolling. Always make sure to add
flex-wrap: wrap
. By default, flexbox stretch its child items to make them equal in height if the direction is set torow
, and it makes them equal in width if the direction is set tocolumn
. -
Each flex item has a
flex-basis
property, which acts as the sizing property for that item. When the value isflex-basis: auto
, the basis is the content’s size. With.item { flex-grow: 1; flex-basis: 0%; }
, each child item will take up the same space as its siblings.flex: 1
is equivalent toflex: 1 1 0
. -
While the default
min-width
value is 0 (zero), for flex items it isauto
. This can make block elements take up much more space than desired, resulting in overflow. The solution is to addmin-width: 0;
to the flex item. -
flex-basis
is more of a suggestion than a hard constraint. At a certain point, there isn’t enough space for all of the elements to sit at their assigned size, and so they have to compromise, in order to avoid an overflow. The default value forflex-grow
is 0. The default value forflex-shrink
is 1. -
How we compare a design against implementation? We can take the original design as an image and place it above the page in the browser. Thanks to CSS backgrounds and pseudo-elements, this is possible. Please make sure that the browser width is equal to the design width and no other element in the same stacking context has a higher
z-index
than the pseudo-element. Also, you will notice that nothing is hoverable or clickable, that’s because the pseudo-element is covering the page. We can allow interactivity by settingpointer-events: none
(the specified HTML element is never the target of pointer events).body { position: relative; } body:after { content: ""; position: absolute; left: 0; top: 0; z-index: 100; width: 100%; height: 100%; background-image: url('example.png'); background-size: 100% auto; background-repeat: no-repeat; opacity: 0.5; pointer-events: none; }
-
You can add infinite borders using
box-shadow
if you want to apply multiple borders on one div.box-shadow
is described by X and Y offsets relative to the element, blur and spread radius, and color. You can set multiple effects separated by commas.img { margin: 40px; width: 90px; border-radius: 50%; /* box-shadow: x-offset y-offset blur spread color */ box-shadow: 0 0 0 10px #817dd1, 0 0 0 20px #5c58aa, 0 0 0 30px #3d3a84, 0 0 0 40px #211f56; }
-
In order for the
postion: sticky
element to function correctly, it needs to have at least one of it’stop
,right
,left
, orbottom
placement properties set. Also look for anyoverflow
property set on any parents of the element. You can’t useoverflow: hidden
,overflow: auto
, oroverflow: scroll
on the parent of aposition: sticky
element. -
If you are using
visible
for eitheroverflow-x
oroverflow-y
and something other thanvisible
for the other, thevisible
value is interpreted asauto
. Say if one is set tovisible
, and the other toauto
orhidden
, then thevisible
is changed toauto
..wrapper { overflow-y: hidden; /* browser will add this by default, resulting in clipping both sides. */ overflow-x: auto; }
-
This is where the
overflow: clip
becomes helpful. It’s supported by all major browsers. If you setoverflow-y
toclip
. Theoverflow-x
value will stay as is (visible
). Now the clipping happens only on the y-axis. -
Position
fixed
doesn’t work withtransform
CSS property. It happens because transform creates a new coordinate system and yourposition: fixed
element becomes fixed to that transformed element. -
Center one and right/left align other element: Grid layout may be the cleanest and most efficient method. There is no need for absolute positioning or flexbox with fake elements.
(above picture comes from @shadeed9)
header { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); } .left-text, .right-text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
-
The styles for flexbox columns are built with the
order
property to reposition the columns. With CSS grid, it isn’t needed at all as we can reorder the layout by positioning an element on any grid lines we want..c-newspaper { grid-template-columns: 1fr 2fr 1fr; } /* The first column is placed from line 2 to line 3 */ .c-newspaper__col:first-child { grid-column: 2/3; } /* The last column is placed from line 3 to line 4 */ .c-newspaper__col:last-child { grid-column: 3/4; }
There’s a lot of advantages to grid, like
minmax()
/repeat()
functions. Grid is also required for any 2D layout as flex can’t handle that, and it tends to be better when you have more intentionally defined values for rows and/or columns. Also, you get thefr
unit which is really handy. -
The
grid-template-areas
property specifies areas within the grid layout (limited to rectangular grid areas). Once the template areas are defined, we can reference them in CSS and assign each named area to its designated element by using thegrid-area
property..page { display: grid; grid-template-columns: 200px 1fr; grid-template-areas: "aside main" "footer footer"; gap: 1rem; } .aside { grid-area: aside; } .main { grid-area: main; } .footer { grid-area: footer; }
-
Have you ever wondered why padding is inconsistent above and below text elements? Each font has a different
line-height
and that comes with a different spacing above and below the text. To fix that, we can add a fake element next to the button’s TextNode, and when a pseudo-element is placed next to it, we can usevertical-align: middle
to center both. This is a much better solution than setting the different top and bottom spacing values..button:before { content: ""; display: inline-block; height: 16px; vertical-align: middle; }
-
A dialog is a component in a web page or app that usually contains an action or some task to perform. Dialogs have a role of
dialog
, which the browser will assign automatically for you when you use the<dialog>
element. You can also create dialogs with ARIA: applyrole="dialog"
to an element like<div>
. If it is a modal dialog, addaria-modal="true"
when it shows, and remove it when it is dismissed. Dialogs can be modal (when shown withdialog.showModal()
) or non modal (when shown withdialog.show()
). When<dialog>
s are modal, the browser will treat the content outside of the dialog as inert, and prevent keyboard focus from reaching web content outside of the dialog. If a<dialog>
is not modal, the other content is not treated as inert. Browsers will close modal dialogs when users pressEscape
. Non-modal dialogs don’t get this default behaviour, developers can add it where it makes sense. Check out the slides about how to build dialogs and popovers.<dialog>
examples demo (default, error, wait, notify, confirm, transitioned, light dismiss, fully customized): https://codepen.io/argyleink/pen/VwJvqrW -
CSS
unset
will remove all properties set directly on the matched element, and revert to inheriting from the cascade - like a parent element or<body>
(if the property naturally inherits from its parent);inherit
is pretty straight-forward that inherits all defined properties from its parent element.revert
will set the property to the user agent stylesheet value — AKA the default browser style.initial
is the nuclear option. This will reset a CSS property as if no CSS rules had been written for that value, which means it’s gonna remove the style all together.<!-- What color is the <p> tag’s text? --> <!-- The answer is red since unset “unsets” the style of the p tag, so it inherits from its parent – body. --> <p class="hello">Hello</p> <style> body { color: red; } p { color: green; } .hello { color: unset; } </style>
Children of parents with
min-height
can’t inherit the height property. The easiest workaround is to addheight: 1px;
to the parent. Alternatively you may setmin-height: inherit;
to the child. -
Stop re-inventing the wheel and just use
<button>
to create a button. If you’re worried about default button styles, useall: unset
. This one line of CSS will strip all default browser styles so you can apply your own. -
Sometimes the web page looks strangely inflated on mobile landscape devices. The reason is that Mobile Safari increases the default font-size when you switch a website from portrait to landscape. The way to control this font-size inflation is with the
-webkit-text-size-adjust
property, which you can set to a percentage which to increase the text size to at the most, toauto
for default behavior or tonone
to prevent zooming text in. Setting it to100%
is equivalent tonone
./* Prevent font size inflation */ html { -moz-text-size-adjust: none; -webkit-text-size-adjust: none; text-size-adjust: none; }
-
The border radius of the outer element should be equal to the sum of the border radius of the inner element and the distance between the two elements. So if the outer element border radius is 20, and there’s a 5px space between the outer element and inner element, the inner element border radius should be 15.
-
Before Chrome 118, font sizes smaller than 10px or so were not rendered as specified, but rounded up if the language was Arabic, Farsi, Japanese, Korean, Thai, Simplified or Traditional Chinese. Developers needed workarounds to render small text, for example by using the
transform
property. From Chrome 118, this limit is ended for all languages, making the seven languages match the rest. -
Less absolute positioning with modern CSS. For example, when we have a card that contains text over an image, we often use
position: absolute
to place the content over the image. This is no longer needed with CSS grid.grid-area: 1 / -1;
(interpreted asgrid-area: 1 / -1 / auto / auto
) places the element on the first row and makes it span all the way to the last column. It’s a common way to make an element stretch across an entire row or column in a grid layout./* By default, CSS grid will create rows automatically based on the content. */ .card { position: relative; display: grid; } .card__thumb, .card__content { grid-column: 1/2; /* we can also use `grid-area: 1/-1` */ grid-row: 1/2; } /* grid-area: 2 / 1 / 2 / 4; */ /* grid-row-start / grid-column-start / grid-row-end / grid-column-end
-
8-digit hex notation (#RRGGBBAA): The first 6 digits are interpreted identically to the 6-digit notation. The last pair of digits, interpreted as a hexadecimal number, specifies the alpha channel of the color, where
00
represents a fully transparent color andff
represent a fully opaque color. Btw, 4-digit notation (#RGBA) is a shorter variant of the 8-digit notation. If there is only one number, it is duplicated:1
means11
,c
meanscc
. -
text-underline-offset
sets the offset distance of an underline text decoration line from its original position. All browsers support this property. Note that it is not part of thetext-decoration
shorthand. -
The
white-space
CSS property sets how white space inside an element is handled. By default, the sequences of white space are collapsed. Newline characters in the source are handled the same as other white space. Usewhite-space: pre-wrap;
to preserve spaces, tabs, and new lines. -
CSS animations are pretty sweet, but they typically require explicit sizes, you couldn’t use the intrinsic sizing keywords like
auto
,min-content
, orfit-content
. From Chrome 129, you can declareinterpolate-size: allow-keywords
on:root
to enable transitioning to and from intrinsic sizing keywords for the entire document. -
If you wanted to pause a CSS Keyframe animation using JavaScript, how would you do it? You can use the element’s
getAnimations
method to get an array of all the animations on the element. From there you can loop through the array and call thepause()
method on each animation.
btn.addEventListener("click", () => {
const animations = document.querySelector(".circle")
.getAnimations();
if (btn.textContent === "Pause Animation") {
animations.forEach((animation) => {
animation.pause();
});
btn.textContent = "Play Animation";
} else {
animations.forEach((animation) => {
animation.play();
});
btn.textContent = "Pause Animation";
}
});
- CSS background image on background color.
{
background-image: url('images/foo.png');
background-color: #6DB3F2;
}
{
background: url('images/foo.png'), #6DB3F2;
}
The second one is not shorthand for the first. In the first method, last property (color) takes precedence. The use of the comma in the background property sets multiple backgrounds which get layered on top of each other. The image will be on top, color underneath (opposite of the first method).
MDN docs: You can apply multiple backgrounds to elements. These are layered atop one another with the first background you provide on top and the last background listed in the back. Only the last background can include a background color. You can do this with both the shorthand
background
property and the individual properties except forbackground-color
.