Files
mal/DESIGN_SYSTEM.md

6.1 KiB

MAL Design System

Overview

This design system is for the MAL anime streaming service. It provides reusable components, design tokens, and patterns for building consistent UI.

Design Tokens

Colors

Token Value Usage
--color-background #080808 Main page background
--color-background-sidebar #0f0f0f Sidebar background
--color-background-header #141414 Sticky header
--color-background-surface #202020 Cards, dropdowns
--color-background-button #1a1a1a Button default state
--color-background-button-hover #252525 Button hover state
--color-foreground-muted #6a6b70 Secondary text, icons
--color-foreground #f8f9fa Primary text
--color-accent #9f7aea Primary accent (violet)
--color-danger #dc2626 Error states, delete actions

Typography

Role Font Size Weight
Title DM Sans 20px 600
Body DM Sans 14px 400
Label DM Sans 14px 500
Small DM Sans 12px 400

Spacing

Token Value
--space-1 0.25rem
--space-2 0.5rem
--space-3 0.75rem
--space-4 1rem
--space-5 1.25rem
--space-6 1.5rem
--space-8 2rem

Components

1. Anime Card (anime_card.gohtml)

A poster card with hover reveal.

Usage:

{{template "anime_card" dict "Anime" . "WithActions" true "IsWatchlist" false}}

Props:

  • Anime - Anime data object
  • WithActions - Show hover overlay with actions (bool)
  • IsWatchlist - Show in-watchlist state (bool)
  • Compact - Hide metadata below (bool)
  • HideTitle - Hide title below image (bool)
  • HasTopBadge - Adjust overlay padding for badge (bool)

States:

  • Default: Image with subtle dark placeholder
  • Hover: Black overlay (80%) reveals title, synopsis, watchlist button

2. Navigation (navigation.gohtml)

Sidebar navigation with active indicator.

Usage:

{{template "navigation" dict "CurrentPath" .CurrentPath}}

Props:

  • CurrentPath - Current page path for active state

Items:

  • Home (/)
  • Browse (/browse)
  • Discover (/discover)
  • Watchlist (/watchlist)

States:

  • Default: Muted gray text
  • Hover: bg-white/5
  • Active: Accent text + left border (w-0.5 bg-accent) + subtle glow

3. Dropdown (dropdown.gohtml)

Custom dropdown using <ui-dropdown> web component.

Usage:

<ui-dropdown class="relative block" data-align="left" data-width="w-48">
  <div data-trigger>
    <button>Trigger</button>
  </div>
  <div data-content class="hidden absolute ...">
    <button>Option 1</button>
    <button>Option 2</button>
  </div>
</ui-dropdown>

Attributes:

  • data-align - left or right alignment
  • data-width - Width (Tailwind class)

Styling:

  • Background: #1a1a1a
  • No border radius
  • Items: 10px vertical padding, hover bg-white/10

4. Filter Bar (filter_bar.gohtml)

Search and filter controls for browse page.

Usage:

{{template "filter_bar" .}}

Controls:

  • Search input (text)
  • Genre dropdown (multi-select)
  • Status dropdown (airing, complete, upcoming)
  • Type dropdown (tv, movie, ova, special, ona)
  • Sort dropdown + order toggle

5. Header (header.gohtml)

Sticky header with logo, search, and user menu.

Usage:

{{template "header" .}}

Sections:

  • Left: Mobile menu toggle, sidebar toggle, logo
  • Center: Search input (desktop only)
  • Right: User avatar dropdown

6. Watchlist Actions (watchlist_actions.gohtml)

Watchlist toggle button.

Usage:

{{template "watchlist_actions" dict "MalID" 123 "IsWatchlist" true}}

Props:

  • MalID - Anime MAL ID
  • IsWatchlist - Current state

States:

  • Not in watchlist: Outline icon, accent on hover
  • In watchlist: Filled icon (fill: currentColor)

7. Video Player (video_player.gohtml)

Video player container for episodes.

Usage:

{{template "video_player" dict "WatchData" .WatchData "TotalEpisodes" .Anime.Episodes}}

Props:

  • WatchData - Video source data
  • TotalEpisodes - Total episode count

8. Continue Watching (continue_watching.gohtml)

Horizontal row for continue watching section.

Usage:

{{template "continue_watching" .ContinueWatching}}

Props:

  • Array of anime with watch progress

Patterns

Button Variants

Primary:

<button class="bg-accent text-black px-4 py-2">Action</button>

Secondary:

<button class="bg-white/5 text-neutral-300 px-4 py-2 hover:bg-white/10">Action</button>

Danger:

<button class="bg-red-500/10 text-red-400 px-4 py-2 hover:bg-red-500/20">Delete</button>

Empty State

<div class="flex flex-col items-center justify-center gap-4 py-24">
  <svg class="h-16 w-16 opacity-30">...</svg>
  <p class="text-neutral-300 font-medium">Title</p>
  <p class="text-neutral-500 text-sm">Description</p>
  <div class="flex gap-3 mt-2">
    <a class="bg-accent/20 text-accent px-5 py-2.5">Action</a>
    <a class="bg-white/10 text-neutral-200 px-5 py-2.5">Secondary</a>
  </div>
</div>

Card Grid

<div class="grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4 2xl:grid-cols-6">
  <!-- Cards -->
</div>

Episode List Item

<a class="flex items-center gap-3 px-3 py-2 hover:bg-white/5 border-l-2 {{if .Filler}}border-l-yellow-500{{end}} {{if .Recap}}border-l-blue-500{{end}} {{if $isCurrent}}bg-accent/20{{end}}">
  <span class="w-10 shrink-0 text-xs font-medium text-neutral-500">EP{{.MalID}}</span>
  <span class="truncate text-sm text-neutral-300">{{.Title}}</span>
</a>

Migration Guide

Adding a New Component

  1. Create template in templates/components/
  2. Document props and usage in this file
  3. Add to component index above

Updating Existing Components

  1. Make changes in templates/components/
  2. Update this documentation
  3. Test across all usage sites

Design Token Changes

  1. Update in static/style.css
  2. Update YAML frontmatter in DESIGN.md
  3. Update this token reference