
"It is raining out there, let's stay here and learn some CSS fundamentals"
Some CSS terminology
Cascade
"Cascade" is what determines how the rules apply over a particular element.
When declarations conflict, the cascade considers three things to resolve the difference:
- Stylesheet origin: Where the styles come from. Your styles are applied in conjunction with the browser's default styles.
- Selector specificity: Which selectors take precedence over which.
- Source order: Order in which styles are declared in the stylesheet
Declaration, block, ruleset, at-rules
A line of css is a declaration. A declaration is made up of a property and a value.
A bunch of declarations within curly braces is called a block. A block is preceded by a selector.
Together, the selector and the block are called a "ruleset".
at-rules are language constructs beginning with an “at” symbol, such as @import rules or @media queries.
Specificity
The most specific ones are the inline styles, because apply only to the element they are in.
Only !important can override this. And if you have a !important in an inline style, nothing can beat it.
The exact rules of specificity are:
- If a selector has more IDs, it wins (that is, it’s more specific).
- If that results in a tie, the selector with the most classes wins.
- If that results in a tie, the selector with the most tag names wins.
** NOTE Pseudo-class selectors (for example, :hover) and attribute selectors (for example, [type="input"]) each have the same specificity as a class selector. The universal selector (*) and combinators (>, +, ~) have no effect on specificity.
For example, if you want to have things right, you must write the next rules in this order, as they all have the same specificity.
a:link {
color: blue;
text-decoration: none;
}
a:visited {
color: purple;
}
a:hover {
text-decoration: underline;
}
a:active {
color: red;
}
Rule of thumb for writing css selectors
- don't use ids. Something you can do to overwrite a class is create another one and add it, so it has more specificity.
- don't use !important.
Well, rules are make to be broken, you know.
Inheritance
Values are passed from parents to children, but not all values are passed. Are mostly the ones related to text and lists.
There are special values related to inheritance that we can use: inherit and initial.
- inherit: Element inherits the value from its parent, despite there is some other selector overriding this value.
- initial: Every CSS property has an initial, or default, value. If you assign the value initial to that property, then it effectively resets to its default value. It’s like a hard-reset of that value. !!SUPER IMPORTANT: Declaring display: initial is equivalent to display: inline. It won’t evaluate to display: block regardless of what type of element you apply it to. That’s because initial resets to the initial value for the property, not the element; inline is the default value for the display property.
Shorthand properties
Shorthand properties are properties that let you set the values of several other properties at one time. For example, font is a shorthand property that let you set several font properties.
This declaration specifies font-style, font-weight, font-size, line-height, and font-family:
font: italic bold 18px/1.2 "Helvetica", "Arial", sans-serif;
Units
For a long time, designers focused on “pixel-perfect” designs. They’d create a tightly defined container, often a centered column around 800 px wide. Then, within these constraints, they’d go about designing more or less like their predecessors did with native applications or print publications.
As technology improved and manufacturers introduced higher-resolution monitors, the pixel-perfect approach slowly started to break down.
That is where responsive design comes in.
Pixel is a slightly misleading name—a CSS pixel does not strictly equate to a monitor’s pixel.
This is notably the case on high-resolution (“retina”) displays.
The relative units are ems and rems. ems is the most broadly used of those two. They are mostly used for padding and margin.
em
1 em means the font size of the current element. The element will take the px per em that should be displaying, so if it's not defined, will be set by the font-size value that inherites.
* This causes a problem when you are nesting elements. If you use em to set font-size, you won't only affect that element,
but all the nested ones. And it becomes a mess. That's why we use an improved unit, the rem:
rem
The root element of the DOM is the "html" element. A selector to target the root is ":root", which is the same as the selector for tag "html" but with class specificity.
rem is short for "root em". Instead of being relative to the current element, rem is relative to the root element.
This way we avoid the problems that we were having with em.
A good default would be to use:
rems for font sizes, pixels for borders, and ems for most other measures, especially paddings, margins, and border radius (though we can use percentages for container widths when necessary).
This would be a good starting point for a project:
:root {
font-size: 0.75em;
}
@media (min-width: 800px) {
:root {
font-size: 0.875em;
}
}
@media (min-width: 1200px) {
:root {
font-size: 1em;
}
}
There are also another relative unit. Those which are relative to the viewport of the browser.
* viewport: The framed area in the browser window where the web page is visible. This excludes the browser’s address bar, toolbars, and status bar, if present.
vh
1/100th of the viewport height
vw
1/100th of the viewport width
vmin
1/100th of the smaller dimension, height or width (IE9 supports vm instead of vmin)
vmax
1/100th of the larger dimension, height or width (not supported in IE or, at the time of writing, Edge)
Sizes can be calculated with the function "calc()" from + - / and * values with same or different units.
calc(5px + 30vh)
CSS variables (aka Custom properties)
Variables that you can set and reuse. Useful for:
- Settign values that will be constants throughout the app.
- Manipulate those values with js. So remember, if you want to manipulate your css values with js, this is the way.
Some key point:
They are defined inside a block.
If its set to an element (for example :root, aka html element), all the elements that inherit from that one can access the variable.
If an inner block changes the variable value, it changes it ONLY for all the elements that are nested inside.
html:
<!DOCTYPE html>
<head>
<link href="./styles.css" rel="stylesheet" type="text/css" />
<link href="./styles_2.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h1>jodete, perro</h1>
<div class="panel">
<h2>Single-origin</h2>
<div class="body">
We have built partnerships with small farms around the world to
hand-select beans at the peak of season. We then careful roast in small
batches to maximize their potential.
</div>
</div>
<aside class="dark">
<div class="panel">
<h2>Single-origin</h2>
<div class="body">
We have built partnerships with small farms around the world to
hand-select beans at the peak of season. We then careful roast in small
batches to maximize their potential.
</div>
</div>
</aside>
</body>
styles:
:root {
--main-bg: red;
--main-color: green;
}
.panel {
font-size: 1rem;
padding: 1em;
border: 1px solid #999;
border-radius: 0.5em;
background-color: var(--main-bg);
color: var(--main-color);
}
.panel > h2 {
margin-top: 0;
font-size: 0.8em;
font-weight: bold;
text-transform: uppercase;
}
.dark {
margin-top: 2em;
padding: 1em;
background-color: #999;
--main-bg: orange;
--main-color: purple;
}
** Notice that the html imports two css files. The css variables set in the first file will be accessible from the second one.
Now, how to get and set with js:
<script type="text/javascript">
var rootElement = document.documentElement;
var styles = getComputedStyle(rootElement);
// get
var mainColor = styles.getPropertyValue('--main-bg');
// set
rootElement.style.setProperty('--main-bg', '#cdf');
</script>
The Box Model
** Magic numbers: numbers that you apply to achieve the result that you want. That is something bad, you should be adding numbers based in what makes logically sense. If you don't know where the number comes from, you won't be able to forsee how it will behave.
By default, box-sizing is set to the value "content-box". This means that any height or width you specify only sets the size of the content box. You can assign a value of border-box to the box sizing instead. That way, the height and width properties set the combined size of the content, padding, and border.
*Applying box-sizing: border-box to an element changes the box model to a more predictable behavior. Setting height or width will control the size of the entire element, including its padding and border (not the margin).
Trick to make all elements to have box-sizing: border-box:
:root {
box-sizing: border-box;
}
*,
::before,
::after {
box-sizing: inherit; // Box sizing isn’t a inherited property, but using the inherit keyword, forces it to be.
}
With the version shown here, you can convert a third-party component into a content-box when necessary by targeting its top-level container. Then
all elements inside the component will inherit the box sizing:
.third-party-component {
box-sizing: content-box;
}
Width percentage is always about the size of the container.
Height percentage is a different matter: typically it’s best to avoid setting explicit heights on elements. Normal document flow is designed to work with a constrained width and an unlimited height.
Contents fill the width of the viewport and then line wrap as necessary. Because of this, the height of a container is organically determined by its contents, not by the container itself.
So it's not normally a good idea to set the height of elements. If you do so it's common that its contents overflow the container. To deal with this we have the property "overflow".
Overflow has 4 possible values:
- visible (default value): All content is visible, even when it overflows the container’s edges.
- hidden: Content that overflows the container’s padding edge is clipped and won’t be visible.
- scroll: Scrollbars are added to the container so the user can scroll to see the remaining content. On some operating systems, both horizontal and vertical scrollbars are added, even if all the content is visible. In this case, the scrollbars will be disabled (grayed).
- auto: Scrollbars are added to the container only if the contents overflow.
You can control only horizontal overflow using the overflow-x property, or vertical
overflow with overflow-y.
Height percentage is normally problematic. The percentage is about the container and the container height is about the size of its children, so it's a catch 22 situation.
So, for percentage-based heights to work, the parent must have an explicitly defined height (!!!!!)
Vertically centering content
Why in Maradona's name doesn’t the property vertical-align work?
Don't expect it to center the contents of the block. The declaration will be ignored by the browser.
A vertical-align declaration only affects inline and table-cell elements.
In inline elements, it controls alignment among other elements on the same line. You can use it to control how an inline image aligns with the neighbouring text, for example.
In table-cell elements, vertical-align controls the alignment of the contents within the cell.
A REALLY nice way -I would call this a trick ;) - to center content within a container is to add same padding-top and padding-bottom, and let the
child find its place. This approach works whether the content inside the container is inline, block, or of any other display value.
header {
padding-top: 4em;
padding-bottom: 4em;
}
margins collapsing
When an element has a top and bottom margins (left and right margins don't collapse), and you put them one over the other, their margins collapse, and the margin rendered is the biggest one of the two.
That way you don't have to worry too much.
** Flexbox margins don't collapse!!
Common problem!! You have a column with buttons. Each button has the same top and bottom margin. Margins collapse. All good so far. But the container has padding (top and bottom, for example), so it adds too much space at the beginning and and at the end of the column of buttons.
To avoid this, we can use:
// This way only .button-link that are followed by another .button-link will apply the rule.
.button-link + .button-link {
margin-top: 1.5em;
}
But there is a better way to deal with this: the "lobotomized owl selector" + * +. Its basically the same but used over all the elements of, for example, the body:
body * + * {
margin-top: 1.5em;
}
*Using the lobotomized owl like this is a tradeoff. It simplifies many margins throughout your page, but you’ll have to override it in places where you don’t want it to apply.
Don't be shy, leave us a comment