API Essentials

At-Rules

Styleframe at-rules enable you to create modern CSS features like container queries, feature detection, font loading, and more with full type safety and auto-complete support.

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;

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.

Pro tip: Use descriptive at-rule names and organize related at-rules together for better maintainability and readability.

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;

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;

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;

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;

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;

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;

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;

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;

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;

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 appropriate unicode-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