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
Feature Detection with @supports
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 Loading with @font-face
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;
}
}
Custom Properties with @property
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
@supportsto 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: swapand appropriateunicode-rangevalues. - Define custom properties with validation using
@propertyfor 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.