At-Rules
Overview
At-rules in Styleframe provide a powerful way to use CSS at-rules with full type safety and auto-complete. You can define media queries, supports queries, font faces, CSS layers, container queries, and more while maintaining the benefits of Styleframe's TypeScript support.
Why use at-rules?
At-rules help you:
- Use modern CSS features: Leverage container queries, CSS layers, feature detection, and other cutting-edge CSS capabilities.
- Maintain type safety: Get auto-complete and instant feedback for all at-rule properties and conditions.
- Create progressive enhancement: Use feature detection to provide fallbacks for browsers that don't support certain features.
- Organize your CSS: Use CSS layers and other organizational at-rules to structure your styles systematically.
Defining At-Rules
You define at-rules using the atRule()
function from your styleframe instance:
import { styleframe } from 'styleframe';
const s = styleframe();
const { atRule, selector } = s;
// Feature detection with @supports
atRule('supports', '(display: grid)', ({ selector }) => {
selector('.grid-container', {
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))',
gap: '1rem',
});
});
// Font loading with @font-face
atRule('font-face', '', {
fontFamily: "'CustomFont'",
src: "url('fonts/custom.woff2') format('woff2')",
fontWeight: 'normal',
fontStyle: 'normal',
fontDisplay: 'swap',
});
export default s;
@supports (display: grid) {
.grid-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
}
}
@font-face {
font-family: 'CustomFont';
src: url('fonts/custom.woff2') format('woff2');
font-weight: normal;
font-style: normal;
font-display: swap;
}
The atRule()
function takes an identifier (the at-rule name), a rule (the at-rule condition or value), and either declarations or a callback function with the styles to apply.
Specialized At-Rule Functions
While the generic atRule()
function handles all at-rules, Styleframe also provides specialized functions for common use cases:
Media Queries
The media()
function is a specialized version of atRule()
for responsive design:
import { styleframe } from 'styleframe';
const s = styleframe();
const { media, selector } = s;
selector('.container', {
padding: '1rem',
});
media('(min-width: 768px)', ({ selector }) => {
selector('.container', {
padding: '2rem',
});
});
export default s;
.container {
padding: 1rem;
}
@media (min-width: 768px) {
.container {
padding: 2rem;
}
}
Read more about media queries.
Keyframes
The keyframes()
function is designed specifically for CSS animations:
import { styleframe, css } from 'styleframe';
const s = styleframe();
const { keyframes, selector, ref } = s;
const fadeInAnimation = keyframes('fade-in', {
'0%': {
opacity: 0,
transform: 'translateY(10px)',
},
'100%': {
opacity: 1,
transform: 'translateY(0)',
},
});
selector('.animated-element', {
animation: css`${ref(fadeInAnimation)} 0.3s ease-out`,
});
export default s;
@keyframes fade-in {
0% {
opacity: 0;
transform: translateY(10px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.animated-element {
animation: fade-in 0.3s ease-out;
}
Read more about keyframes.
Examples
@supports
Feature Detection with
Use @supports
to progressively enhance your styles based on browser capability:
import { styleframe } from 'styleframe';
const s = styleframe();
const { atRule, selector } = s;
// Base fallback styles
selector('.layout', {
display: 'flex',
flexWrap: 'wrap',
});
// Enhanced styles for grid-supporting browsers
atRule('supports', '(display: grid)', ({ selector }) => {
selector('.layout', {
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
gap: '2rem',
});
});
// Feature detection with multiple conditions
atRule('supports', '(display: grid) and (gap: 1rem)', ({ selector }) => {
selector('.modern-layout', {
display: 'grid',
gap: '1rem',
});
});
// Negative feature detection
atRule('supports', 'not (display: grid)', ({ selector }) => {
selector('.fallback-layout', {
display: 'flex',
flexDirection: 'column',
});
});
export default s;
.layout {
display: flex;
flex-wrap: wrap;
}
@supports (display: grid) {
.layout {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
}
}
@supports (display: grid) and (gap: 1rem) {
.modern-layout {
display: grid;
gap: 1rem;
}
}
@supports not (display: grid) {
.fallback-layout {
display: flex;
flex-direction: column;
}
}
@font-face
Font Loading with
Define custom fonts with optimal loading characteristics:
import { styleframe } from 'styleframe';
const s = styleframe();
const { atRule, selector } = s;
// Multiple font formats for better compatibility
atRule('font-face', '', {
fontFamily: "'InterVariable'",
src: `
url('fonts/Inter-Variable.woff2') format('woff2'),
url('fonts/Inter-Variable.woff') format('woff')
`,
fontWeight: '100 900',
fontStyle: 'normal',
fontDisplay: 'swap',
});
// Font with unicode range for performance
atRule('font-face', '', {
fontFamily: "'NotoSans'",
src: "url('fonts/NotoSans-Latin.woff2') format('woff2')",
unicodeRange: 'U+0000-00FF, U+0131, U+0152-0153',
fontDisplay: 'swap',
});
// Use the custom font
selector('.custom-typography', {
fontFamily: "'InterVariable', system-ui, sans-serif",
});
export default s;
@font-face {
font-family: 'InterVariable';
src: url('fonts/Inter-Variable.woff2') format('woff2'),
url('fonts/Inter-Variable.woff') format('woff');
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'NotoSans';
src: url('fonts/NotoSans-Latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153;
font-display: swap;
}
.custom-typography {
font-family: 'InterVariable', system-ui, sans-serif;
}
CSS Layers for Organization
Use CSS layers to control the cascade and organize your styles:
import { styleframe } from 'styleframe';
const s = styleframe();
const { atRule } = s;
// Establish layer order
atRule('layer', 'reset, base, components, utilities', {});
// Base layer styles
atRule('layer', 'base', ({ selector }) => {
selector('body', {
fontFamily: 'system-ui, sans-serif',
lineHeight: '1.6',
color: '#333',
});
selector('h1, h2, h3', {
fontWeight: '700',
marginBottom: '0.5em',
});
});
// Component layer styles
atRule('layer', 'components', ({ selector }) => {
selector('.button', {
padding: '0.75rem 1.5rem',
borderRadius: '0.5rem',
border: 'none',
cursor: 'pointer',
});
});
// Utility layer styles (highest priority)
atRule('layer', 'utilities', ({ selector }) => {
selector('.text-center', {
textAlign: 'center',
});
selector('.hidden', {
display: 'none',
});
});
export default s;
@layer reset, base, components, utilities;
@layer base {
body {
font-family: system-ui, sans-serif;
line-height: 1.6;
color: #333;
}
h1, h2, h3 {
font-weight: 700;
margin-bottom: 0.5em;
}
}
@layer components {
.button {
padding: 0.75rem 1.5rem;
border-radius: 0.5rem;
border: none;
cursor: pointer;
}
}
@layer utilities {
.text-center {
text-align: center;
}
.hidden {
display: none;
}
}
Container Queries for Component-Based Responsive Design
Use container queries to create truly responsive components:
import { styleframe } from 'styleframe';
const s = styleframe();
const { atRule, selector } = s;
// Define container context
selector('.card-container', {
containerType: 'inline-size',
containerName: 'card',
});
selector('.card', {
padding: '1rem',
backgroundColor: '#f9f9f9',
borderRadius: '8px',
});
// Container query for small containers
atRule('container', 'card (min-width: 250px)', ({ selector }) => {
selector('.card', {
padding: '1.5rem',
});
selector('.card-title', {
fontSize: '1.25rem',
});
});
// Container query for larger containers
atRule('container', 'card (min-width: 400px)', ({ selector }) => {
selector('.card', {
display: 'flex',
alignItems: 'center',
gap: '1rem',
});
selector('.card-content', {
flex: '1',
});
});
export default s;
.card-container {
container-type: inline-size;
container-name: card;
}
.card {
padding: 1rem;
background-color: #f9f9f9;
border-radius: 8px;
}
@container card (min-width: 250px) {
.card {
padding: 1.5rem;
}
.card-title {
font-size: 1.25rem;
}
}
@container card (min-width: 400px) {
.card {
display: flex;
align-items: center;
gap: 1rem;
}
.card-content {
flex: 1;
}
}
@property
Custom Properties with
Define custom CSS properties with validation and inheritance:
import { styleframe } from 'styleframe';
const s = styleframe();
const { atRule, selector } = s;
// Define a custom property for colors
atRule('property', '--brand-color', {
syntax: '<color>',
inherits: 'true',
initialValue: '#006cff',
});
// Define a custom property for spacing
atRule('property', '--spacing-unit', {
syntax: '<length>',
inherits: 'false',
initialValue: '1rem',
});
// Define a custom property for animation duration
atRule('property', '--animation-speed', {
syntax: '<time>',
inherits: 'true',
initialValue: '0.3s',
});
// Use the custom properties
selector('.component', {
backgroundColor: 'var(--brand-color)',
padding: 'var(--spacing-unit)',
transition: `all var(--animation-speed) ease`,
});
export default s;
@property --brand-color {
syntax: '<color>';
inherits: true;
initial-value: #006cff;
}
@property --spacing-unit {
syntax: '<length>';
inherits: false;
initial-value: 1rem;
}
@property --animation-speed {
syntax: '<time>';
inherits: true;
initial-value: 0.3s;
}
.component {
background-color: var(--brand-color);
padding: var(--spacing-unit);
transition: all var(--animation-speed) ease;
}
Advanced Usage
Nesting At-Rules
You can nest at-rules to create complex conditions and better organization:
import { styleframe } from 'styleframe';
const s = styleframe();
const { atRule, selector } = s;
selector('.responsive-component', {
padding: '1rem',
}, ({ atRule }) => {
// Nested supports query
atRule('supports', '(display: grid)', {
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))',
}, ({ atRule }) => {
// Further nested media query
atRule('media', '(min-width: 768px)', {
gap: '2rem',
gridTemplateColumns: 'repeat(3, 1fr)',
});
});
});
export default s;
.responsive-component {
padding: 1rem;
@supports (display: grid) {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
@media (min-width: 768px) {
gap: 2rem;
grid-template-columns: repeat(3, 1fr);
}
}
}
Using Variables with At-Rules
Combine at-rules with Styleframe variables for consistent, maintainable styles:
import { styleframe } from 'styleframe';
const s = styleframe();
const { variable, ref, atRule, selector } = s;
const breakpointMd = variable('breakpoint--md', '768px');
const colorPrimary = variable('color--primary', '#006cff');
const spacingLg = variable('spacing--lg', '2rem');
// Media query using variables
atRule('media', `(min-width: ${breakpointMd.value})`, ({ selector }) => {
selector('.hero', {
backgroundColor: ref(colorPrimary),
padding: ref(spacingLg),
});
});
export default s;
:root {
--breakpoint--md: 768px;
--color--primary: #006cff;
--spacing--lg: 2rem;
}
@media (min-width: var(--breakpoint--md)) {
.hero {
background-color: var(--color--primary);
padding: var(--spacing--lg);
}
}
Best Practices
- Use feature detection with
@supports
to provide progressive enhancement and graceful fallbacks. - Organize styles with layers to maintain a predictable cascade and make debugging easier.
- Leverage container queries for truly component-based responsive design that adapts to container size rather than viewport size.
- Optimize font loading with
font-display: swap
and appropriateunicode-range
values. - Define custom properties with validation using
@property
for better developer experience and browser optimization. - Group related at-rules logically and use meaningful names for better maintainability.
FAQ
atRule()
function supports all standard CSS at-rules including @media
, @supports
, @font-face
, @keyframes
, @layer
, @container
, @property
, and more.media()
and keyframes()
are convenience wrappers around atRule()
that provide better ergonomics for common use cases. Use atRule()
for any at-rule not covered by the specialized functions.atRule
function in the context parameter.@supports
queries to detect feature support and provide appropriate fallbacks. This ensures your styles work across different browsers while taking advantage of modern features where available.selector
, variable
, ref
, and nested atRule
calls, allowing you to build complex styling logic.Selectors
Styleframe selectors allow you to create CSS rules with type-safe property definitions. They provide a powerful way to style your components.
Media Queries
Styleframe media queries enable responsive design with type-safe breakpoint definitions. Create adaptive layouts that work seamlessly across all device sizes.