# Anti-Patterns to Avoid When Using the Astryx Design System: 10 Critical Mistakes

> Learn 10 critical anti-patterns to avoid with the Astryx design system. Discover how to leverage StyleX features for optimal performance and theme consistency.

- Repository: [Meta/astryx](https://github.com/facebook/astryx)
- Tags: best-practices
- Published: 2026-07-02

---

**Avoid hard-coding CSS values, using CSS nesting operators, and toggling classes manually; instead leverage StyleX design tokens, `stylex.when` helpers, and `stylex.props()` conditionals to maintain compile-time optimizations and theme consistency.**

The Astryx design system from Meta provides a CSS-native styling layer through StyleX alongside a library of reusable components. Understanding the anti-patterns to avoid when using the Astryx design system prevents performance degradation and broken design-token consistency. This guide analyzes the specific mistakes documented in the [`packages/core/README.md`](https://github.com/facebook/astryx/blob/main/packages/core/README.md) PATTERN section and provides corrected implementations using first-party APIs.

## Design Token Anti-Patterns

Hard-coding values like `border-radius: 8px` or hex colors breaks theme switching and consistency. According to the [`packages/core/README.md`](https://github.com/facebook/astryx/blob/main/packages/core/README.md) guidelines, always use `stylex.defineVars` and `stylex.createTheme` to establish semantic tokens rather than hard-coding radii or colors.

**❌ Hard-coded radius values**

```tsx
<div style={{ borderRadius: '8px' }}>Inconsistent</div>

```

**✅ Use design tokens**

```tsx
import { stylex } from '@astryxdesign/stylex';

const styles = stylex.create({
  container: { 
    borderRadius: stylex.vars.radiusMedium 
  },
});

export const Component = () => (
  <div className={styles.container}>Theme-aware</div>
);

```

## CSS Architecture Mistakes

Astryx deliberately avoids CSS nesting. Using `&` operators produces unexpected specificity and complicates auditing. The [`internal/stylex-capabilities/CAPABILITIES.md`](https://github.com/facebook/astryx/blob/main/internal/stylex-capabilities/CAPABILITIES.md) documentation confirms that StyleX handles state through JavaScript rather than nested selectors.

**❌ CSS nesting with `&`**

```css
/* This bypasses StyleX processing */
.button {
  &::after { content: ''; }
}

```

**✅ Use `stylex.when` helpers**

```tsx
import { stylex } from '@astryxdesign/stylex';

const styles = stylex.create({
  button: {
    ...stylex.when.ancestor(':hover', {
      after: { content: '""' },
    }),
  },
});

```

Similarly, avoid manual fallback CSS. Writing separate fallback rules duplicates logic that StyleX already handles via `stylex.firstThatWorks()`.

**❌ Manual fallback rules**

```tsx
const styles = stylex.create({
  element: {
    display: 'flex',
    display: '-webkit-flex', // Redundant manual fallback
  },
});

```

**✅ Leverage built-in fallbacks**

```tsx
const styles = stylex.create({
  element: {
    display: stylex.firstThatWorks('flex', '-webkit-flex'),
  },
});

```

## Dynamic Styling Anti-Patterns

Inline `style` objects bypass StyleX's static analysis, losing compile-time optimizations and potentially duplicating CSS. Never use `className={cond ? 'a' : 'b'}` toggling, which undermines the declarative nature of StyleX and risks class name collisions.

**❌ Inline styles and manual class toggling**

```tsx
// ❌ Bypasses static analysis
<div style={{ color: dynamicColor }}>

// ❌ Imperative class toggling
<div className={isActive ? 'active' : 'inactive'}>

```

**✅ Conditional props pattern**

Use `stylex.props()` with boolean expressions to conditionally apply generated classes:

```tsx
const styles = stylex.create({
  base: { color: 'gray' },
  active: { color: 'blue' },
});

export const Component = ({ isActive }: { isActive: boolean }) => (
  <div className={stylex.props(styles.base, isActive && styles.active)}>
    Composable styles
  </div>
);

```

For ancestor and descendant states, skipping `stylex.when` leads to fragile selectors or JavaScript state syncing. Use the dedicated helpers instead:

```tsx
const styles = stylex.create({
  control: {
    ...stylex.when.ancestor(':hover', { opacity: 0.8 }),
    ...stylex.when.descendant(':focus', { borderColor: 'blue' }),
    ...stylex.when.siblingBefore(':checked', { background: 'green' }),
  },
});

```

## Component Scope and Integration

Using generic `<a>` elements instead of the `Link` component prevents proper routing integration with Next.js or React Router. Always render links via `useLinkComponent()` or within a `LinkProvider` to preserve the abstraction layer so the underlying router can be swapped.

For form controls, avoid the default marker which leaks hover and focus states from outer containers (causing popover glitches). Create component-scoped markers using `stylex.defineMarker()`:

```tsx
const marker = stylex.defineMarker();
const styles = stylex.create({
  control: { marker: true },
});

```

## Performance and Maintenance

Redundant CSS bloats stylesheets and hurts performance. Rely on StyleX's automatic deduplication and keep selectors lean using the `stylex.props()` composability model. The [`internal/eslint-plugin-astryx/README.md`](https://github.com/facebook/astryx/blob/main/internal/eslint-plugin-astryx/README.md) file contains ESLint rules that automatically catch many of these anti-patterns during development.

Before modifying components, query the documentation using the CLI command shown in [`packages/cli/README.md`](https://github.com/facebook/astryx/blob/main/packages/cli/README.md):

```bash
$ASTRYX component Button --dense

```

## Summary

- **Never hard-code values**—use `stylex.defineVars` and theme tokens for radii, colors, and spacing.
- **Avoid CSS nesting** with `&`—prefer `stylex.when.ancestor()` and `stylex.when.descendant()` for state-based styling.
- **Skip inline styles**—encode dynamic values within `stylex.create()` and apply conditionally via `stylex.props()`.
- **Don't toggle classes manually**—use the declarative `stylex.props(condition && styles.variant)` pattern.
- **Use `stylex.firstThatWorks()`** instead of manual CSS fallbacks.
- **Render links via `useLinkComponent()`** rather than generic `<a>` tags to preserve routing integration.
- **Scope form markers** with `stylex.defineMarker()` to prevent state leakage from containers.

## Frequently Asked Questions

### What happens if I use inline style objects with Astryx?

Inline `style` props bypass StyleX's static analysis, which prevents compile-time optimizations and can lead to duplicated CSS in the final bundle. Always encode dynamic values inside `stylex.create()` functions or use the `stylex.props(condition && styles.x)` pattern to maintain design system guarantees.

### How do I handle hover states without CSS nesting?

Use `stylex.when.ancestor(':hover')` or `stylex.when.descendant(':focus')` to express parent-child relationships. This maintains the flat CSS structure that StyleX compiles while avoiding the specificity issues associated with nested `&` selectors, as documented in [`packages/core/README.md`](https://github.com/facebook/astryx/blob/main/packages/core/README.md).

### Why should I avoid manual class name toggling?

Toggling classes via `className={cond ? 'a' : 'b'}` undermines StyleX's declarative model and risks class name collisions. The `stylex.props()` API safely composes generated class names and only applies them when conditions are true, ensuring consistency with the design system.

### How do I verify I'm not using anti-patterns in my codebase?

The `internal/eslint-plugin-astryx` package provides ESLint rules that catch common anti-patterns automatically. Additionally, use the CLI command `$ASTRYX component <Name> --dense` to query component documentation before implementation, as documented in [`packages/cli/README.md`](https://github.com/facebook/astryx/blob/main/packages/cli/README.md).