# Skill: Frontend Styling SCSS

Skill ID: `frontend-styling-scss`

Rules
- SCSS source path is `/css/`; do not create `/scss/` directories.
- Class naming: BEM with plugin prefix (`presszone-translate-*`).
- Variables: plugin-prefixed SCSS variables (`$presszone-translate-*`).
- Colors, spacing, radius, z-index, breakpoints come from variables/tokens only.
- Use namespaced z-index and breakpoint variables; avoid generic `$z-index` and `$breakpoint-*`.
- Use class-based variants for runtime states/settings.
- Keep dark mode overrides co-located with `body.dark-mode &` nesting.
- Avoid CSS custom properties for feature logic (`var(--*)`); prefer SCSS vars + state classes.
- Avoid hardcoded colors, magic numbers, deep selector chains, float/clearfix layout patterns.
- Use Flexbox/Grid.
- Avoid `!important` unless overriding unavoidable core/third-party rules.
- Reuse shared keyframes/animation tokens; do not duplicate animation blocks.
- Canonical shared keyframe: `presszone-multilingual-spin` (defined in `_base.scss`). NEVER define `@keyframes spin`, `mpz-spin`, `tpz-spin`, or any variant — always reference the canonical one.
- Skeleton loader widths must use CSS size-variant classes (e.g., `skeleton-text--sm`, `skeleton-text--lg`), never inline `style="width: Npx"`.
- Fixed-position elements (ghost drag elements, tooltips) must get their positioning from a CSS class, not inline JS styles.

Accessibility
- Visible focus indicators.
- Contrast targets: 4.5:1 normal text, 3:1 large text.
- Layout remains usable at 200% zoom.
- Respect `prefers-reduced-motion`.
- Do not encode status by color alone.

Code examples

```scss
// css/components/_translation-card.scss
@use '../tokens/variables' as *;

// Tokens are defined in shared variables files.
// Do not hardcode colors/sizes in component files.

.presszone-translate-card {
  display: grid;
  gap: $presszone-translate-space-3;
  padding: $presszone-translate-space-4;
  background: $presszone-translate-color-bg;
  color: $presszone-translate-color-text;
  border: 1px solid $presszone-translate-color-border;
  border-radius: $presszone-translate-radius-md;

  body.dark-mode & {
    background: $presszone-translate-color-bg-dark;
    color: $presszone-translate-color-text-dark;
    border-color: $presszone-translate-color-border-dark;
  }

  &__title {
    font-weight: 600;
  }

  &--is-loading {
    opacity: 0.7;
    pointer-events: none;
  }

  &--status-error {
    border-color: $presszone-translate-color-status-error;
  }

  &--status-pending {
    border-color: $presszone-translate-color-status-pending;
  }

  &--status-complete {
    border-color: $presszone-translate-color-status-complete;
  }
}

@media (min-width: $presszone-translate-breakpoint-md) {
  .presszone-translate-card {
    grid-template-columns: 1fr auto;
    align-items: center;
  }
}

@media (prefers-reduced-motion: reduce) {
  .presszone-translate-card,
  .presszone-translate-card * {
    transition-duration: 0s;
    animation-duration: 0s;
    animation-iteration-count: 1;
  }
}

.presszone-translate-button:focus-visible {
  outline: $presszone-translate-focus-width solid $presszone-translate-color-focus;
  outline-offset: $presszone-translate-focus-offset;
}
```

```scss
// State class pattern (recommended)
.presszone-translate-job {
  &--status-pending { border-left: $presszone-translate-border-strong solid $presszone-translate-color-status-pending; }
  &--status-complete { border-left: $presszone-translate-border-strong solid $presszone-translate-color-status-complete; }
  &--status-failed { border-left: $presszone-translate-border-strong solid $presszone-translate-color-status-error; }
}

// Avoid: inline style="border-left-color: ..."
```

Build
- `npm run build:css`
- If admin SCSS changed: `cd admin && npm run build`

Mistakes to avoid
| Mistake | Fix |
|---|---|
| Using `/scss/` folders | Keep SCSS in `/css/` |
| CSS custom properties for logic | Use SCSS vars + explicit classes |
| Hardcoded dark-mode colors | Use vars with `.dark-mode &` nesting |
| Class chaining like `.a.b` | Use single-purpose BEM classes |
| Duplicated keyframes | Use `presszone-multilingual-spin` from `_base.scss` — never define new spin keyframes |
| Skeleton widths via inline style | Use CSS size-variant classes (`skeleton-text--sm/md/lg`) |
| Ghost/fixed elements styled via JS | Define positioning in CSS class, not `.style.*` |
| Deep nesting / `!important` overuse | Flatten selectors and raise specificity correctly |
| CSS Grid: percentage columns + `auto`/`max-content` on utility columns | Percentages don't fill 100% of the grid — leftover space inflates `auto`/`max-content` columns. Use `1fr` on the main content column (e.g., Title) so it absorbs all extra space, keeping utility columns (Actions, Characters) compact |
| Missing reduced-motion rules | Add `@media (prefers-reduced-motion: reduce)` |
| Forgetting CSS build | Run `npm run build:css` |
| Using generic `$z-index`/`$breakpoint-*` names | Use plugin namespaced vars |
| Inline style values for dynamic states | Switch to explicit state classes |
