CSS Advanced Content
This content has been divided into two parts for better readability:
- Part 1 (Current): CSS Architecture, Preprocessors, Custom Properties, and Advanced Selectors
- Part 2: Advanced Layouts, Advanced Animations, CSS Optimization, Future CSS, and Practice Exercises
CSS Architecture
CSS architecture refers to the organization of your CSS code. A well-structured CSS architecture makes your code more maintainable, scalable, and easier to understand.
Common CSS Architecture Methodologies
BEM (Block, Element, Modifier)
BEM is a naming convention that makes your CSS more readable and understandable, while also solving issues with specificity.
/* Block component */
.card {
background-color: white;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
/* Element that depends upon the block */
.card__title {
font-size: 1.5rem;
margin-bottom: 0.5rem;
}
.card__image {
width: 100%;
height: auto;
}
.card__content {
padding: 1rem;
}
/* Modifier that changes the style of the block */
.card--featured {
border-left: 5px solid gold;
}
.card--dark {
background-color: #333;
color: white;
}
HTML implementation:
<div class="card card--featured">
<img class="card__image" src="image.jpg" alt="Card image">
<div class="card__content">
<h2 class="card__title">Card Title</h2>
<p class="card__text">Card content goes here...</p>
</div>
</div>
SMACSS (Scalable and Modular Architecture for CSS)
SMACSS categorizes CSS rules into five types: Base, Layout, Module, State, and Theme.
/* Base rules */
body, h1, h2, p, ul, li {
margin: 0;
padding: 0;
}
a {
text-decoration: none;
color: #0066cc;
}
/* Layout rules */
.l-header {
width: 100%;
position: sticky;
top: 0;
z-index: 100;
}
.l-sidebar {
width: 25%;
float: left;
}
.l-main {
width: 75%;
float: right;
}
/* Module rules */
.nav {
background-color: #333;
}
.nav-item {
display: inline-block;
padding: 10px 15px;
}
/* State rules */
.is-active {
font-weight: bold;
color: #fff;
}
.is-hidden {
display: none;
}
/* Theme rules */
.theme-dark {
background-color: #333;
color: #fff;
}
.theme-light {
background-color: #fff;
color: #333;
}
OOCSS (Object-Oriented CSS)
OOCSS focuses on separating structure from skin and container from content.
/* Structure */
.btn {
display: inline-block;
padding: 0.5em 1em;
border-radius: 4px;
cursor: pointer;
}
/* Skin */
.btn-primary {
background-color: #0066cc;
color: white;
border: 1px solid #0055aa;
}
.btn-secondary {
background-color: #f0f0f0;
color: #333;
border: 1px solid #ddd;
}
/* Usage */
/* <button class="btn btn-primary">Primary Button</button> */
/* <button class="btn btn-secondary">Secondary Button</button> */
Atomic CSS
Atomic CSS (also known as Functional CSS) uses small, single-purpose classes that are named based on their visual function.
/* Atomic classes */
.m-0 { margin: 0; }
.mt-1 { margin-top: 0.25rem; }
.mb-2 { margin-bottom: 0.5rem; }
.p-3 { padding: 1rem; }
.text-center { text-align: center; }
.flex { display: flex; }
.items-center { align-items: center; }
.bg-blue { background-color: blue; }
.text-white { color: white; }
.rounded { border-radius: 0.25rem; }
/* Usage */
/* <div class="flex items-center p-3 bg-blue text-white rounded">
<h2 class="m-0">Title</h2>
<p class="mt-1 mb-2">Content</p>
</div> */
CSS File Organization
Organizing your CSS files is crucial for maintainability. Here's a common approach:
styles/
├── main.css # Main CSS file that imports all others
├── base/
│ ├── _reset.css # CSS reset or normalize
│ ├── _typography.css # Typography rules
│ └── _variables.css # CSS variables
├── layout/
│ ├── _grid.css # Grid system
│ ├── _header.css # Header styles
│ └── _footer.css # Footer styles
├── components/
│ ├── _buttons.css # Button styles
│ ├── _cards.css # Card styles
│ └── _forms.css # Form styles
├── pages/
│ ├── _home.css # Home page specific styles
│ └── _about.css # About page specific styles
└── utils/
├── _helpers.css # Helper classes
└── _mixins.css # Mixins (for preprocessors)
CSS Namespacing
Namespacing helps to avoid conflicts and provides context for your CSS classes.
/* Component namespace */
.c-button { /* Component */ }
.c-card { /* Component */ }
/* Utility namespace */
.u-hidden { /* Utility */ }
.u-text-center { /* Utility */ }
/* State namespace */
.is-active { /* State */ }
.has-error { /* State */ }
/* JavaScript namespace */
.js-modal-trigger { /* JavaScript hook */ }
.js-form-submit { /* JavaScript hook */ }
CSS Architecture Best Practices
- Single Responsibility Principle: Each class should have a single responsibility
- Separation of Concerns: Separate structure from skin, content from container
- Consistent Naming: Use a consistent naming convention
- Avoid Deep Nesting: Keep selector specificity low
- Document Your Code: Add comments to explain complex code
- Use a Style Guide: Create and follow a style guide for your project
- Modular Approach: Build reusable, modular components
CSS Preprocessors
CSS preprocessors extend CSS with features like variables, nesting, mixins, and functions. They help you write more maintainable and reusable CSS code.
Popular CSS Preprocessors
- Sass: The most mature and feature-rich preprocessor
- Less: Similar to Sass but with a syntax closer to CSS
- Stylus: Offers a more flexible syntax with optional colons and semicolons
- PostCSS: A tool for transforming CSS with JavaScript plugins
Sass Features
Sass (Syntactically Awesome Style Sheets) is one of the most popular CSS preprocessors. It comes in two syntaxes: SCSS (Sassy CSS) and the indented syntax (Sass).
Variables
// SCSS Syntax
$primary-color: #0066cc;
$secondary-color: #f0f0f0;
$font-stack: 'Helvetica', 'Arial', sans-serif;
$base-font-size: 16px;
$spacing-unit: 8px;
body {
font-family: $font-stack;
font-size: $base-font-size;
color: $primary-color;
}
.button {
background-color: $primary-color;
padding: $spacing-unit * 2;
margin-bottom: $spacing-unit * 3;
}
Nesting
// SCSS Syntax
.card {
background-color: white;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
&__header {
padding: $spacing-unit * 2;
border-bottom: 1px solid $secondary-color;
h2 {
margin: 0;
font-size: 1.5rem;
}
}
&__body {
padding: $spacing-unit * 2;
}
&__footer {
padding: $spacing-unit * 2;
border-top: 1px solid $secondary-color;
}
// Modifier
&--featured {
border-left: 5px solid gold;
}
}
Mixins
// SCSS Syntax
@mixin flex-center {
display: flex;
align-items: center;
justify-content: center;
}
@mixin truncate-text {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
@mixin responsive-font($min-size, $max-size, $min-width, $max-width) {
font-size: $min-size;
@media (min-width: $min-width) {
font-size: calc(#{$min-size} + #{strip-unit($max-size - $min-size)} * ((100vw - #{$min-width}) / #{strip-unit($max-width - $min-width)}));
}
@media (min-width: $max-width) {
font-size: $max-size;
}
}
// Usage
.hero {
@include flex-center;
height: 100vh;
&__title {
@include responsive-font(2rem, 4rem, 320px, 1200px);
}
&__subtitle {
@include truncate-text;
max-width: 300px;
}
}
Functions
// SCSS Syntax
@function strip-unit($number) {
@if type-of($number) == 'number' and not unitless($number) {
@return $number / ($number * 0 + 1);
}
@return $number;
}
@function rem($pixels, $context: 16) {
@return ($pixels / $context) * 1rem;
}
@function color-contrast($color) {
$r: red($color);
$g: green($color);
$b: blue($color);
$yiq: (($r * 299) + ($g * 587) + ($b * 114)) / 1000;
@return if($yiq >= 128, #000, #fff);
}
// Usage
.button {
font-size: rem(16);
padding: rem(12) rem(24);
background-color: $primary-color;
color: color-contrast($primary-color);
}
Partials and Imports
// _variables.scss
$primary-color: #0066cc;
$secondary-color: #f0f0f0;
// _mixins.scss
@mixin flex-center {
display: flex;
align-items: center;
justify-content: center;
}
// main.scss
@import 'variables';
@import 'mixins';
body {
color: $primary-color;
background-color: $secondary-color;
}
.container {
@include flex-center;
}
Control Directives
// SCSS Syntax
$theme: 'dark';
// If statement
@if $theme == 'dark' {
body {
background-color: #333;
color: #fff;
}
} @else {
body {
background-color: #fff;
color: #333;
}
}
// For loop
@for $i from 1 through 5 {
.col-#{$i} {
width: 20% * $i;
}
}
// Each loop
$sizes: (small: 0.5rem, medium: 1rem, large: 2rem);
@each $name, $size in $sizes {
.margin-#{$name} {
margin: $size;
}
.padding-#{$name} {
padding: $size;
}
}
// While loop
$i: 1;
@while $i <= 5 {
.item-#{$i} {
width: 100% / $i;
}
$i: $i + 1;
}
PostCSS
PostCSS is a tool for transforming CSS with JavaScript plugins. It can be used to add modern CSS features, automate routine tasks, and more.
/* Before PostCSS processing */
.example {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
color: color(#0066cc alpha(80%));
user-select: none;
}
/* After PostCSS processing with autoprefixer, postcss-color-function, etc. */
.example {
display: -ms-grid;
display: grid;
-ms-grid-columns: (minmax(250px, 1fr))[auto-fill];
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
color: rgba(0, 102, 204, 0.8);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
Popular PostCSS Plugins
- Autoprefixer: Adds vendor prefixes to CSS rules
- postcss-preset-env: Converts modern CSS into something browsers understand
- cssnano: Compresses and optimizes CSS
- postcss-import: Inlines @import rules
- postcss-nested: Unwraps nested rules
- postcss-custom-properties: Polyfill for CSS custom properties
Setting Up a CSS Preprocessor
Here's a basic setup for using Sass with npm:
// Install Sass
npm install sass --save-dev
// Add script to package.json
{
"scripts": {
"sass": "sass src/scss:dist/css --watch"
}
}
// Run Sass compiler
npm run sass
And here's a basic setup for PostCSS:
// Install PostCSS and plugins
npm install postcss postcss-cli autoprefixer postcss-preset-env cssnano --save-dev
// Create postcss.config.js
module.exports = {
plugins: [
require('autoprefixer'),
require('postcss-preset-env')({ stage: 1 }),
require('cssnano')({ preset: 'default' })
]
}
// Add script to package.json
{
"scripts": {
"postcss": "postcss src/css/*.css --dir dist/css --watch"
}
}
// Run PostCSS
npm run postcss
CSS Custom Properties (Variables)
CSS custom properties (also known as CSS variables) allow you to store values that you can reuse throughout your stylesheet.
Declaring and Using Custom Properties
/* Declaring custom properties */
:root {
--primary-color: #0066cc;
--secondary-color: #f0f0f0;
--font-family: 'Helvetica', 'Arial', sans-serif;
--base-font-size: 16px;
--spacing-unit: 8px;
--border-radius: 4px;
--transition-duration: 0.3s;
}
/* Using custom properties */
body {
font-family: var(--font-family);
font-size: var(--base-font-size);
line-height: 1.5;
color: #333;
}
.button {
background-color: var(--primary-color);
color: white;
padding: calc(var(--spacing-unit) * 2) calc(var(--spacing-unit) * 4);
border-radius: var(--border-radius);
transition: background-color var(--transition-duration) ease;
}
.button:hover {
background-color: color-mix(in srgb, var(--primary-color) 80%, black);
}
Scoping Custom Properties
Custom properties can be scoped to specific elements, allowing you to create component-specific variables or override global variables.
/* Global variables */
:root {
--text-color: #333;
--background-color: white;
}
/* Light theme (default) */
body {
color: var(--text-color);
background-color: var(--background-color);
}
/* Dark theme */
.theme-dark {
--text-color: white;
--background-color: #333;
}
/* Component-specific variables */
.card {
--card-padding: 16px;
--card-border-color: #ddd;
padding: var(--card-padding);
border: 1px solid var(--card-border-color);
border-radius: var(--border-radius);
}
.card--compact {
--card-padding: 8px;
}
Fallback Values
You can provide fallback values for custom properties in case they are not defined or supported.
/* Using fallback values */
.element {
/* If --brand-color is not defined, use #0066cc */
color: var(--brand-color, #0066cc);
/* Nested fallbacks */
padding: var(--element-padding, var(--global-padding, 16px));
}
Manipulating Custom Properties with JavaScript
One of the advantages of custom properties is that they can be manipulated with JavaScript.
// Get a custom property value
const rootStyles = getComputedStyle(document.documentElement);
const primaryColor = rootStyles.getPropertyValue('--primary-color').trim();
console.log('Primary color:', primaryColor);
// Set a custom property value
document.documentElement.style.setProperty('--primary-color', '#ff0000');
// Change theme based on user preference
const themeToggle = document.getElementById('theme-toggle');
themeToggle.addEventListener('change', function() {
if (this.checked) {
document.documentElement.style.setProperty('--text-color', 'white');
document.documentElement.style.setProperty('--background-color', '#333');
} else {
document.documentElement.style.setProperty('--text-color', '#333');
document.documentElement.style.setProperty('--background-color', 'white');
}
});
Responsive Design with Custom Properties
Custom properties can be particularly useful for responsive design, allowing you to change values at different breakpoints.
/* Base variables */
:root {
--container-width: 100%;
--heading-font-size: 24px;
--content-padding: 16px;
}
/* Tablet breakpoint */
@media (min-width: 768px) {
:root {
--container-width: 750px;
--heading-font-size: 32px;
--content-padding: 24px;
}
}
/* Desktop breakpoint */
@media (min-width: 1024px) {
:root {
--container-width: 980px;
--heading-font-size: 40px;
--content-padding: 32px;
}
}
/* Using the variables */
.container {
width: var(--container-width);
margin: 0 auto;
padding: 0 var(--content-padding);
}
h1 {
font-size: var(--heading-font-size);
}
Theming with Custom Properties
Custom properties are excellent for implementing theme systems.
/* Base theme variables */
:root {
/* Colors */
--color-primary: #0066cc;
--color-secondary: #f0f0f0;
--color-accent: #ff6b6b;
--color-text: #333;
--color-text-light: #666;
--color-background: white;
--color-border: #ddd;
/* Typography */
--font-family-base: 'Helvetica', 'Arial', sans-serif;
--font-family-heading: 'Georgia', serif;
--font-size-base: 16px;
--line-height-base: 1.5;
/* Spacing */
--spacing-unit: 8px;
--spacing-xs: calc(var(--spacing-unit) * 0.5);
--spacing-sm: var(--spacing-unit);
--spacing-md: calc(var(--spacing-unit) * 2);
--spacing-lg: calc(var(--spacing-unit) * 3);
--spacing-xl: calc(var(--spacing-unit) * 4);
/* Borders */
--border-radius-sm: 2px;
--border-radius-md: 4px;
--border-radius-lg: 8px;
--border-width: 1px;
/* Shadows */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
/* Transitions */
--transition-duration-fast: 0.15s;
--transition-duration-normal: 0.3s;
--transition-duration-slow: 0.5s;
--transition-timing-function: ease;
}
/* Dark theme */
.theme-dark {
--color-primary: #4d9fff;
--color-secondary: #333;
--color-accent: #ff8f8f;
--color-text: #f0f0f0;
--color-text-light: #aaa;
--color-background: #222;
--color-border: #444;
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.3);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.3);
}
/* High contrast theme */
.theme-high-contrast {
--color-primary: #0000ff;
--color-secondary: #000;
--color-accent: #ff0000;
--color-text: #000;
--color-text-light: #333;
--color-background: #fff;
--color-border: #000;
--border-width: 2px;
}
Advanced Techniques with Custom Properties
Calculated Values
:root {
--base-size: 16px;
--scale-ratio: 1.25;
--size-xs: calc(var(--base-size) / var(--scale-ratio));
--size-sm: var(--base-size);
--size-md: calc(var(--base-size) * var(--scale-ratio));
--size-lg: calc(var(--size-md) * var(--scale-ratio));
--size-xl: calc(var(--size-lg) * var(--scale-ratio));
}
h1 { font-size: var(--size-xl); }
h2 { font-size: var(--size-lg); }
h3 { font-size: var(--size-md); }
p { font-size: var(--size-sm); }
small { font-size: var(--size-xs); }
Custom Property Toggles
/* Using custom properties as toggles */
.accordion {
--is-open: 0;
}
.accordion.is-open {
--is-open: 1;
}
.accordion__content {
max-height: calc(var(--is-open) * 500px);
opacity: var(--is-open);
overflow: hidden;
transition: max-height 0.3s ease, opacity 0.3s ease;
}
Advanced CSS Selectors
CSS provides a wide range of selectors that allow you to target elements with great precision.
Attribute Selectors
Attribute selectors allow you to select elements based on their attributes and attribute values.
/* Elements with a specific attribute */
[data-role] {
/* Targets any element with a data-role attribute */
}
/* Elements with a specific attribute value */
[data-role="button"] {
/* Targets elements with data-role="button" */
}
/* Elements with an attribute value that starts with a specific string */
[href^="https://"] {
/* Targets links that start with https:// */
}
/* Elements with an attribute value that ends with a specific string */
[href$=".pdf"] {
/* Targets links to PDF files */
}
/* Elements with an attribute value that contains a specific string */
[href*="example"] {
/* Targets links that contain "example" in the URL */
}
/* Elements with an attribute value that contains a specific word */
[class~="card"] {
/* Targets elements with a class that includes the word "card" */
}
/* Elements with a hyphen-separated attribute value starting with a specific string */
[lang|="en"] {
/* Targets elements with lang="en" or lang="en-US", etc. */
}
Pseudo-Classes
Pseudo-classes select elements based on their state or position.
/* User action pseudo-classes */
a:hover { /* When the user hovers over a link */ }
a:active { /* When the user activates a link (e.g., clicks it) */ }
a:focus { /* When the link has focus (e.g., tabbed to) */ }
a:visited { /* When the link has been visited */ }
/* Form state pseudo-classes */
input:focus { /* When the input has focus */ }
input:disabled { /* When the input is disabled */ }
input:enabled { /* When the input is enabled */ }
input:checked { /* When the checkbox or radio button is checked */ }
input:required { /* When the input is required */ }
input:optional { /* When the input is optional */ }
input:valid { /* When the input is valid */ }
input:invalid { /* When the input is invalid */ }
input:in-range { /* When the input value is within the specified range */ }
input:out-of-range { /* When the input value is outside the specified range */ }
input:placeholder-shown { /* When the input is showing a placeholder */ }
/* Structural pseudo-classes */
li:first-child { /* The first child element */ }
li:last-child { /* The last child element */ }
li:nth-child(2) { /* The second child element */ }
li:nth-child(odd) { /* Odd-numbered child elements (1, 3, 5, etc.) */ }
li:nth-child(even) { /* Even-numbered child elements (2, 4, 6, etc.) */ }
li:nth-child(3n+1) { /* Every 3rd element, starting from the 1st */ }
li:nth-last-child(2) { /* The second-to-last child element */ }
li:only-child { /* Elements that are the only child of their parent */ }
p:first-of-type { /* The first element of its type */ }
p:last-of-type { /* The last element of its type */ }
p:nth-of-type(2) { /* The second element of its type */ }
p:nth-last-of-type(2) { /* The second-to-last element of its type */ }
p:only-of-type { /* Elements that are the only ones of their type */ }
/* Other pseudo-classes */
:root { /* The root element (usually html) */ }
:empty { /* Elements that have no children */ }
:not(.active) { /* Elements that don't match the selector */ }
:is(h1, h2, h3) { /* Elements that match any of the selectors */ }
:where(h1, h2, h3) { /* Similar to :is(), but with 0 specificity */ }
:has(> img) { /* Elements that contain at least one element matching the selector */ }
Pseudo-Elements
Pseudo-elements create "virtual" elements that don't exist in the HTML but can be styled with CSS.
/* First line of text */
p::first-line {
font-weight: bold;
}
/* First letter of text */
p::first-letter {
font-size: 2em;
float: left;
margin-right: 0.1em;
}
/* Content before an element */
.card::before {
content: "★";
color: gold;
margin-right: 0.5em;
}
/* Content after an element */
.external-link::after {
content: " ↗";
font-size: 0.8em;
}
/* Selection styling */
::selection {
background-color: #0066cc;
color: white;
}
/* Placeholder text */
input::placeholder {
color: #999;
font-style: italic;
}
/* List markers */
li::marker {
color: #0066cc;
font-weight: bold;
}
Combinators
Combinators allow you to select elements based on their relationship to other elements.
/* Descendant combinator (space) */
.container p {
/* Selects all p elements inside .container */
}
/* Child combinator (>) */
.container > p {
/* Selects all p elements that are direct children of .container */
}
/* Adjacent sibling combinator (+) */
h2 + p {
/* Selects p elements that immediately follow h2 elements */
}
/* General sibling combinator (~) */
h2 ~ p {
/* Selects all p elements that follow h2 elements */
}
/* Combining multiple selectors */
.container > h2 + p {
/* Selects p elements that immediately follow h2 elements that are direct children of .container */
}
Complex Selector Examples
Here are some examples of complex selectors that combine multiple techniques:
/* Select all external links */
a[href^="http"]:not([href*="example.com"]) {
color: #0066cc;
}
/* Style the first paragraph after each heading */
h2 + p::first-line {
font-weight: bold;
}
/* Style list items based on their position */
ul > li:nth-child(odd) {
background-color: #f0f0f0;
}
/* Style form elements based on their state */
input:not([type="submit"]):focus {
border-color: #0066cc;
outline: none;
box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.3);
}
/* Style elements with specific data attributes */
[data-status="success"] {
color: green;
}
[data-status="error"] {
color: red;
}
/* Style elements based on their content */
p:has(> strong) {
background-color: #f0f0f0;
}
/* Style elements based on their position in the document */
:is(h2, h3, h4):not(:first-child) {
margin-top: 2em;
}
Specificity
Specificity determines which CSS rule is applied when multiple rules target the same element. Understanding specificity is crucial for writing maintainable CSS.
/* Specificity hierarchy (from lowest to highest) */
/* Type selectors (e.g., h1) and pseudo-elements (e.g., ::before) */
/* Specificity: 0-0-1 */
p { color: black; }
p::first-line { color: blue; }
/* Class selectors (e.g., .example), attribute selectors (e.g., [type="text"]), and pseudo-classes (e.g., :hover) */
/* Specificity: 0-1-0 */
.text { color: red; }
[type="text"] { color: green; }
p:hover { color: orange; }
/* ID selectors (e.g., #example) */
/* Specificity: 1-0-0 */
#title { color: purple; }
/* Inline styles */
/* Specificity: 1-0-0-0 */
Inline styled text
/* !important (avoid using this when possible) */
/* Overrides all other styles */
p { color: yellow !important; }
Calculating Specificity
Specificity is calculated as a four-part value: a-b-c-d
- a: Inline styles (1 if present, 0 if not)
- b: Number of ID selectors
- c: Number of class selectors, attribute selectors, and pseudo-classes
- d: Number of type selectors and pseudo-elements
/* Examples of specificity calculations */
/* Specificity: 0-0-0-1 */
p { color: black; }
/* Specificity: 0-0-1-0 */
.text { color: red; }
/* Specificity: 0-1-0-0 */
#title { color: purple; }
/* Specificity: 0-0-1-1 */
p.text { color: green; }
/* Specificity: 0-1-0-1 */
p#title { color: blue; }
/* Specificity: 0-1-1-0 */
#title.text { color: orange; }
/* Specificity: 0-1-1-1 */
p#title.text { color: yellow; }
/* Specificity: 0-0-2-1 */
p.text.active { color: pink; }
/* Specificity: 0-0-1-2 */
div p.text { color: brown; }
/* Specificity: 0-0-2-2 */
div.container p.text { color: cyan; }