Advanced CSS
---
theme: default
routerMode: hash
favicon: https://oim3690.github.io/favicon.svg
titleTemplate: "%s - OIM3690"
title: "CSS Advanced (Reference)"
info: |
Reference deck: CSS variables, specificity & !important, transitions & animations, transforms, frameworks (Tailwind/Bootstrap), CSS art, modern CSS
<br><br>
<b>Custom:</b> <kbd>t</kbd> Timer · <kbd>T</kbd> Start/pause · <kbd>w</kbd> Draw
<br>
<b>Slidev:</b> <kbd>o</kbd> Overview · <kbd>d</kbd> Dark mode · <kbd>f</kbd> Fullscreen · <kbd>←</kbd> <kbd>→</kbd> Navigate
---
# CSS Advanced
## Variables · Animation · Frameworks · Modern CSS
---
layout: center
---
# CSS Variables
---
# CSS Variables (Custom Properties)
Define a value once, reuse it everywhere:
```css
:root {
--brand: #3b82f6;
--gap: 16px;
}
.button { background: var(--brand); }
.card { padding: var(--gap); }
```
Change `--brand` in one place and every use updates.
---
# Why Variables Matter
- **Theming**: one source of truth for colors, spacing, fonts
- **Dark mode**: swap variable values instead of rewriting rules
- AI generates them constantly in design systems
> 🤖 **AI tip:** When AI hands you a `:root` block of `--` variables, re-theme by editing those values, not by touching each component.
---
layout: center
---
# Specificity & `!important`
---
# When Your CSS "Won't Apply"
Usually specificity: a more specific rule is winning.
```css
/* AI wrote this */
#header .nav a { color: blue; }
/* You added this, but it loses */
a { color: red; }
```
The ID plus class selector outranks the plain `a`, so the link stays blue.
---
# `!important` (Use Sparingly)
`!important` overrides normal specificity:
```css
a { color: red !important; }
```
It works, but it starts an "!important war" that is hard to undo later.
> 🤖 **AI tip:** Before reaching for `!important`, open DevTools, find the winning rule, and match its specificity instead.
---
layout: center
---
# Transitions & Animations
---
# Transitions: Smooth Changes
A transition eases a property from one value to another:
```css
.button {
background: #3b82f6;
transition: background 0.3s ease;
}
.button:hover { background: #1d4ed8; }
```
Instead of snapping, the color fades over 0.3 seconds.
---
# ▶️ Transition
Edit the CSS on the left, then hover the button. Try changing `0.2s` to `2s`:
<CssPlayground :code="`.demo-btn {
padding: 0.6rem 1.5rem;
border: none;
border-radius: 8px;
background: #3b82f6;
color: white;
cursor: pointer;
transition: transform 0.2s ease, background 0.2s ease;
}
.demo-btn:hover {
transform: scale(1.1);
background: #1d4ed8;
}`">
<button class="demo-btn">Hover me</button>
</CssPlayground>
---
# Timing Functions
```css
transition: transform 0.5s ease; /* slow start and end (default) */
transition: transform 0.5s linear; /* constant speed */
transition: transform 0.5s ease-in; /* slow start */
transition: transform 0.5s ease-out; /* slow end */
transition: transform 0.5s ease-in-out; /* slow both ends */
```
`ease` is the default and fits most cases.
---
# Keyframes: Multi-Step Animations
Keyframes play automatically, in steps:
```css
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.1); }
}
.badge { animation: pulse 2s ease-in-out infinite; }
```
---
# Animation Properties
```css
.element {
animation-name: pulse;
animation-duration: 2s;
animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
animation-direction: alternate;
}
/* Shorthand */
.element { animation: pulse 2s ease-in-out infinite alternate; }
```
---
# ▶️ Keyframes
Edit the animations and watch them loop. Try changing `1.4s` or `scale(1.4)`:
<CssPlayground :code="`.demo-pulse {
width: 44px; height: 44px; border-radius: 50%;
background: #ef4444;
animation: pulse 1.4s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.4); opacity: 0.5; }
}
.demo-spin {
width: 44px; height: 44px; border-radius: 50%;
border: 4px solid #cbd5e1; border-top-color: #3b82f6;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}`">
<div class="flex gap-8 items-center">
<div class="demo-pulse"></div>
<div class="demo-spin"></div>
</div>
</CssPlayground>
---
# Transitions vs Keyframes
| Use a transition when | Use keyframes when |
|---|---|
| Reacting to hover, click, focus | The animation runs on its own |
| A simple one-step change | Multiple steps or a sequence |
| Interactive feedback | Spinners, looping attention effects |
---
# Performance & Accessibility
- Animate **`transform`** and **`opacity`** (GPU-friendly)
- Avoid animating `width`, `height`, `margin` (they trigger layout)
- Respect users who prefer less motion:
```css
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
```
---
layout: center
---
# Transforms
---
# Transforms
`transform` moves, scales, or rotates without affecting layout. Hover the boxes, then edit the values:
<CssPlayground :code="`.tf-box {
width: 78px; height: 78px;
display: flex; align-items: center; justify-content: center;
background: #3b82f6; color: white; border-radius: 8px;
cursor: pointer;
transition: transform 0.25s ease;
}
.tf-scale:hover { transform: scale(1.15); }
.tf-rotate:hover { transform: rotate(15deg); }
.tf-move:hover { transform: translateY(-12px); }`">
<div class="flex gap-4">
<div class="tf-box tf-scale">scale</div>
<div class="tf-box tf-rotate">rotate</div>
<div class="tf-box tf-move">move</div>
</div>
</CssPlayground>
---
layout: center
---
# CSS Frameworks
---
# Why CSS Frameworks?
Writing CSS from scratch for every project is slow. Frameworks give you:
- **Pre-built styles**: buttons, cards, forms ready to use
- **Consistent design** out of the box
- **Responsive by default**
> Frameworks are what AI reaches for. Learning to **read** them matters more than memorizing them.
---
# Two Approaches
| Component-based | Utility-first |
|---|---|
| Pre-built components | Small single-purpose classes |
| Add a class, get a component | Combine classes to build anything |
| **[Bootstrap](https://getbootstrap.com/)** | **[Tailwind CSS](https://tailwindcss.com/)** |
| Sites tend to look similar | Unique designs, more classes in HTML |
---
# Recognize the Framework
**Bootstrap** (component classes):
```html
<button class="btn btn-primary">Click</button>
```
**Tailwind** (utility classes, what AI usually generates):
```html
<button class="bg-blue-500 text-white px-4 py-2 rounded">Click</button>
```
`btn` and `container` and `col-` mean Bootstrap. `bg-`, `text-`, `px-`, `py-`, `rounded` mean Tailwind.
---
# Reading Tailwind Classes
Class names describe what they do:
| Class | Meaning | Vanilla CSS |
|---|---|---|
| `bg-blue-500` | Blue background | `background: #3b82f6` |
| `text-white` | White text | `color: white` |
| `px-4` | Horizontal padding | `padding-left/right: 1rem` |
| `rounded` | Rounded corners | `border-radius: 0.25rem` |
| `hover:bg-blue-700` | Darker on hover | `:hover { background: ... }` |
You do not need to memorize these. You need to **read** them.
---
# Same Card: Vanilla vs Tailwind
Both produce the same card. Tailwind keeps the styling in the HTML:
<div class="grid grid-cols-[1.9fr_1fr] gap-8 items-center mt-4">
<div>
```css
/* Vanilla CSS */
.card {
padding: 20px;
border-radius: 8px;
background: #eff6ff;
box-shadow: 0 2px 8px rgba(0,0,0,0.12);
}
```
```html
<!-- Tailwind -->
<div class="bg-blue-50 p-5 rounded-lg shadow-md">...</div>
```
</div>
<div class="flex justify-center">
<div class="demo-cardx">Product card</div>
</div>
</div>
<style scoped>
.demo-cardx {
padding: 18px 30px; border-radius: 8px; background: #eff6ff;
box-shadow: 0 2px 8px rgba(0,0,0,0.12); font-weight: 600;
}
</style>
---
# shadcn/ui and What AI Generates
[shadcn/ui](https://ui.shadcn.com/) is a popular set of polished components built on **React plus Tailwind**. The buttons below mimic its clean, neutral look:
<div class="flex gap-4 justify-center my-6">
<button class="sb-btn sb-primary">Button</button>
<button class="sb-btn sb-outline">Outline</button>
</div>
When **Copilot**, **Cursor**, or **v0.dev** generate UI, expect:
- **Tailwind** classes most of the time
- **shadcn/ui** components for more complex UIs
- Sometimes plain CSS or Bootstrap
<style scoped>
.sb-btn {
padding: 8px 18px; border-radius: 6px; font-size: 0.85em; font-weight: 500;
cursor: pointer; border: 1px solid transparent; transition: background 0.15s;
}
.sb-primary { background: #18181b; color: white; }
.sb-primary:hover { background: #2e2e35; }
.sb-outline { background: white; color: #18181b; border-color: #e4e4e7; }
.sb-outline:hover { background: #f4f4f5; }
</style>
---
# What You Need to Know
You do **not** need to:
- Memorize Tailwind classes
- Set up a framework yourself
You **do** need to:
- **Recognize** framework classes
- **Understand** what the AI-generated code does
- **Modify** it confidently (change a color, adjust spacing)
> 🤖 **AI tip:** Ask AI *"explain each class in this line"* to decode unfamiliar Tailwind fast.
---
layout: center
---
# CSS Art
---
# CSS Art: No JavaScript, Just CSS
Pure CSS can draw surprisingly complex images:
- [Pure CSS Landscape](https://codepen.io/ivorjetski/pen/xxGYWQG) ([how it was built](https://www.youtube.com/watch?v=rUCVBNNyjC4))
- [Portrait](https://codepen.io/ivorjetski/pen/dBYWWZ)
- [Still life](https://codepen.io/ivorjetski/pen/xMJoYO)
These are built entirely from gradients, shadows, and shapes. Not practical for daily work, but they show how far CSS reaches.
---
layout: center
---
# Modern CSS
---
# Dark Mode
Adapt to the user's system preference, or let them toggle it. Try the toggle:
```css
@media (prefers-color-scheme: dark) {
:root { --bg: #111; --text: #eee; }
}
```
<div class="dm-demo flex flex-col items-center gap-3 my-5">
<label class="cursor-pointer select-none"><input type="checkbox"> toggle dark mode</label>
<div class="dm-box">Preview text</div>
</div>
<style scoped>
.dm-demo .dm-box {
background: #f5f5f5; color: #111; padding: 14px 32px; border-radius: 8px;
transition: background 0.3s, color 0.3s;
}
.dm-demo:has(input:checked) .dm-box { background: #111; color: #eee; }
</style>
---
# Pseudo-Elements
`::before` and `::after` insert content without touching the HTML. Edit the star and the badge:
<CssPlayground :code="`.pe-tag {
position: relative;
font-weight: 600;
font-size: 1.4em;
}
.pe-tag::before {
content: '★ ';
color: #f59e0b;
}
.pe-tag::after {
content: 'NEW';
position: absolute; top: -12px; right: -46px;
background: #ef4444; color: white;
font-size: 0.5em;
padding: 2px 6px; border-radius: 4px;
}`">
<span class="pe-tag">Inbox</span>
</CssPlayground>
---
# Fluid Sizing: clamp() and Container Queries
```css
/* Font scales with the viewport, within bounds */
h1 { font-size: clamp(1.5rem, 4vw, 3rem); }
/* Style based on the container's width, not the screen */
@container (min-width: 400px) {
.card { display: flex; }
}
```
`clamp()` replaces many media queries. Container queries make a component responsive to its own space.
---
# References
- [MDN: CSS Custom Properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties)
- [MDN: CSS Transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions) · [CSS Animations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations)
- [Tailwind CSS](https://tailwindcss.com/) · [Bootstrap](https://getbootstrap.com/) · [shadcn/ui](https://ui.shadcn.com/)
- [Animate.css](https://animate.style/): a pre-built animation library
Topics Covered
- transitions and keyframe animations
- CSS variables
- frameworks (Tailwind intro)
- dark mode, pseudo-elements, clamp