.page-header {
/* shorthand */
border-bottom: 1px solid #eee;
}
p {
font-family: Arial; /* font */
font-size: 14px; /* font */
color: #333; /* color */
margin: 10px 0; /* layout */
}
.btn-outline {
color: #563d7c; /* color */
border-color: #563d7c; /* color */
}
$font-size: 10px;
$font-family: Helvetica, sans-serif;
body {
font: $font-size $font-family;
}
.mark{
font-size: 1.5 * $font-size;
}
@mixin clearfix {
&:after {
display: block;
content: '';
clear: both;
}
}
.sidebar{
@include clearfix;
}
.main{
@include clearfix;
}
.sidebar:after {
display: block;
content: '';
clear: both;
}
.main:after {
display: block;
content: '';
clear: both;
}
// menu
.nav {
> li {
> a:hover {
background-color: red;
}
// submenu
> ul {
background-color: #fff;
> li > a:hover {
background-color: black;
}
}
}
}
// Sass
$color: #f00;
$images: "../img";
@mixin clearfix {
&:after {
content: " ";
display: block;
clear: both;
}
}
body {
color: $color;
background: url("#{img}/1.png");
@include clearfix;
}
// Less
@color: #f00;
@images: "../img";
.clearfix() {
&:after {
content: " ";
display: block;
clear: both;
}
}
body {
color: @color;
background: url("@{img}/1.png");
.clearfix;
}
Let's take a look
:root { color: red; }
i {border: 1px solid currentColor;}
:root { color: red; }
i {border: 1px solid red;}
Usual text: Lorem ipsum with
link1 and
link2
Disclaimer: I created images
1.png and
2.png
but they are not meant to
represent any specific company
p {color: #333;}
p.disclaimer {color: red;}
/* Set links to reuse currentcolor
for color and border-color */
p a {
text-decoration: none;
color: currentcolor;
border-bottom:
1px dashed currentcolor;
}
/* declaration */
--VAR_NAME: <declaration-value>;
/* usage */
var(--VAR_NAME)
/* root element selector (global scope), e.g. <html> */
:root {
/* CSS variables declarations */
--main-color: #ff00ff;
--main-bg: rgb(200, 255, 255);
}
body {
/* use the variable */
color: var(--main-color);
}
Didn't expect the "--"? There was a reason.
:root{
--main-color: #4d4e53;
--main-bg: rgb(255, 255, 255);
--logo-border-color: rebeccapurple;
--header-height: 68px;
--content-padding: 10px 20px;
--base-line-height: 1.428571429;
--transition-duration: .35s;
--external-link: "external link";
--margin-top: calc(2vh + 20px);
}
:root{
--foo: if(x > 5) this.width = 10;
}
If the variable has already been assigned to
- it wonβt be re-assigned
p {
--p-margin: 5px;
margin: var(--p-margin, 0 0 10px); /* 5px */
}
If it doesnβt have a value yet
- it will be given one.
p {
margin: var(--p-margin, 0 0 10px);/* 0 0 10px */
}
.block {
--block-text: 'This is my block';
--block-highlight-text: var(--block-text)' with highlight';
}
.block__highlight:before {
content: var(--block-highlight-text);
/*This is my block with highlight*/
}
As for any other CSS property, you can apply "initial" and "inherit" values
.with-reset {
--bgcolor: initial;/* RESETS THE VALUE for the scope */
--color: green;/* CHANGES THE VALUE */
--border: inherit;/* INHERITS THE VALUE for the scope */
}
My block is
awesome
Follow usual CSS cascade rules:
:root{
--global-var: 1em;
/* --global-var is available globally */
}
.block {
--block-var: 1.5em;
/* --global-var and --block-var are available */
}
.block__highlight {
--block-highlight-var: 2rem;
/* --global-var , --block-var and --block-highlight-var */
font-size: var(--block-highlight-font-size);
}
/* SCSS: scope depends on
the selectors
structure in the code
"{}" define scope */
$font-size: 20px;
.block{
$font-size: 42px;
/* font-size: $font-size; */
}
.block__highlight{
font-size: $font-size;
}
/* CSS: scope depends
on the current HTML DOM structure
*/
:root{
--font-size: 20px;
}
.block{
--font-size: 42px;
}
.block__highlight{
font-size: var(--font-size);
}
/* Global scope (usually <html/>) */
:root {
--bg: #f00;
}
/* var is reassigned when media query is applied */
@media screen and (min-width: 800px) {
:root { --bg: #f00; }
}
/* and on body hover is reassigned for <body/> */
/* (but will be the same for <html/> as it's a parent scope) */
body:hover {
--bg: #ff0;
}
:root {
--screen-category: 'desktop';
}
@media screen and (max-width: 1024px) {
:root { --screen-category: 'tablet'; }
}
@media screen and (max-width: 640px) {
:root { --screen-category: 'phone'; }
}
body:after {
content: '--screen-category : 'var(--screen-category);
}
:root {
--block-font-size: 1rem;
}
.block__highlight {
/* DOESN'T WORK */
font-size: var(--block-font-size)*1.5;
}
CSS calc(π€) to the rescue (for values)!
:root {
--block-font-size: 1rem;
}
.block__highlight {
/* WORKS */
font-size: calc(var(--block-font-size)*1.5);
}
To pass variables from CSS to JS we used to use workarounds or hacks to write JSON in CSS
.breakpoints-data {
font-family: '{"phone":"480px","tablet":"800px"}';
}
.breakpoints-data {
--phone: 480px;
--tablet: 800px;
}
const breakpointsData = document.querySelector('.breakpoints-data');
// GET
const phone = getComputedStyle(breakpointsData)
.getPropertyValue('--phone');
// SET
breakpointsData.style
.setProperty('--phone', 'custom');
@supports ( (--a: 0)) {
/* supported */
}
@supports ( not (--a: 0)) {
/* not supported */
}
const isSupported = window.CSS && window.CSS.supports &&
window.CSS.supports('--a', 0);
/* e.g. load a CSS file generated by a preprocessor */
if(!isSupported){
removeCss('css-custom-properties.css')
loadCss('without-css-custom-properties.css');
}
Color schema switcher based on CSS custom property values
Cannot be done by a preprocessor without generating additional code
Custom Property:
:root{
/* --property: value; */
--VAR: <declaration-value>;
}
Custom properties can hold more than just values- they can also be used to hold sets of declarations:
:root{
/*
--property: {
property1: value1;
property...: value...;
}
*/
--MIXIN: {
/* style declaration 1 */
/* style declaration ... */
};
}
:root {
--pink-schema: {
color: #6A8759;
background-color: #F64778;
}
}
body{
@apply --pink-schema;
}
@apply rule takes these sets of declarations and inlines them in another style rule
Everything from CSS variables (scopes, usage from JS etc.) is applicable for Custom Sets of Properties
:root {
--clearfix: {
display: table;
clear: both;
content: '';
};
}
.clearfix:after{
@apply --clearfix;
}
:root {
--overflow-ellipsis: {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
};
}
.overflow-box{
@apply --overflow-ellipsis;
}
:root {
--triangle-to-bottom-size: 50px;
--triangle-to-bottom-color: #007bff;
--triangle-to-bottom: {
width: 0;
height: 0;
border-style: solid;
border-width: var(--triangle-to-bottom-size)
var(--triangle-to-bottom-size)
0 var(--triangle-to-bottom-size);
border-color: var(--triangle-to-bottom-color)
transparent transparent transparent;
};
}
.triangle-to-bottom {
@apply --triangle-to-bottom;
}
:root {
--triangle-to-bottom-size: 50px;
--triangle-to-bottom: {
/* STYLES */
border-bottom-width: var(--triangle-to-bottom-size);
};
}
.triangle-to-bottom {
--triangle-to-bottom-size: 8px;
@apply --triangle-to-bottom;
/* but still 50px size is applied for border-bottom-width */
}
Discussion to change this behavior is in progress
* {/* has zero specificity */
/* prevents the property inheritance from outer scopes */
--clip-path: initial;
-webkit-clip-path: var(--clip-path);
clip-path: var(--clip-path);
}
Every time you need clip-path:
header {/* any selector like this overrides the "*" */
/* assign the prop value for the scope */
--clip-path: polygon(0% 0%, 100% 0%, 100% 100%%, 0% 100%);
}
table.colortable td {
text-align:center;
}
table.colortable td.upper {
text-transform:uppercase;
}
table.colortable td:first-child,
table.colortable td:first-child+td {
border:1px solid #000;
}
/* Dropdown menu on hover */
ul {
/* direct nesting (& MUST be the first part of selector)*/
& > li {
color: #000;
& > ul { display: none; }
&:hover {
color: #f00;
& > ul { display: block; }
}
}
}
@nest < selector (MUST CONTAIN a nesting selector '&') >
.foo {
color: black;
@nest body.loading & {
opacity: 0.5;
}
@nest :not(&) {
color: white;
}
}
.foo {
color: black;
}
body.loading .foo {
opacity: 0.5;
}
:not(.foo) {
color: white;
}
a {
@media (min-width: 30em) {
color: yellow;
}
}
@media (min-width: 30em) {
a {
color: yellow
}
}
Bugs in old browsers with the order of inclusion
Before requests didn't go in parallel
For HTTP1x good practice is file concatenation.
With coming of HTTP/2 rules will be cnanged.
/* Formal syntax */
@import [ <string> | <url> ] [<media-query-list>]?;
You can easily apply media queries for different stylesheets.
@import url("print.css") print;
@import "mobile.css" (max-width: 728px);
The linked resources are loaded only when condition is met.
/* SYNTAX */
:matches( selector[, selector]* )
A functional pseudo-class taking a selector list as its argument.
.nav:matches(.side,.top) .links:matches(:hover, :focus) {
color: #BADA55;
}
/* Same thing as this... */
.nav.side .links:hover,
.nav.top .links:hover,
.nav.side .links:focus,
.nav.top .links:focus {
color: #BADA55;
}
/* SYNTAX */
@custom-selector: <custom-selector> <selector-list>;
Example:
@custom-selector :--text-inputs input[type="text"],
input[type="password"];
:--text-inputs.disabled,
:--text-inputs[disabled] {
opacity: 0.5
}
Same as:
input[type="text"].disabled,
input[type="password"].disabled,
input[type="text"][disabled],
input[type="password"][disabled] {
opacity: 0.5
}
/* SYNTAX */
color( <color> <color-adjuster>* )
color(
red /* from red */
blackness(+25%) /* to 25% more black than red */
blackness(+25%) /* to 50% more black than red */
blackness(-50%) /* to red again */
hue(+ 30deg) /* to orange */
hue(- 30deg) /* to red again */
);
/* SYNTAX */
@custom-media --NAME <media-query-list>;
How to use:
@custom-media --tablet (min-width: 800px) and (max-width: 1024px);
@media (--tablet){
.custom-media-queries{
background-color: red;
}
}
@media (min-width: 800px) and (max-width: 1024px) {
.media-queries-range{
background-color: red;
}
}
Use:
@media (width >= 800px) and (width <= 1024px) {
.media-queries-range{ background-color: red; }
}
With custom media queries:
@custom-media --tablet (width >= 800px) and (width <= 1024px);
@media (--tablet){
/* STYLES */
}
//...
postcss: {
options: {
processors: [
require('autoprefixer')({
browsers: ['last 2 versions']
})
]
},
//...
//...
postcss: {
options: {
processors: [
require('postcss-cssnext')({
browsers: ['last 2 versions']
})
]
},
//...
1) Preprocessor π€
2) Preprocessor+PostCSS+ability to use new CSS features π
3) PostCSS +all the CSS additions power (with limitations) π
4) You have pure CSS with everything π