Füllhorn – Solarpunk UI Components

## touch_app Buttons

### Button Varianten

.btn-primary (Fern) | .btn-secondary (Outline) | .btn-accent (Amber) | .btn-ghost

### Danger & Disabled

### Icon Buttons

## label Chips

### Unit Chips (einfacher Toggle)

g kg ml l Stück Packung
.chip-unit – Active: Fern BG, White Text

### Category Chips (mit Ring-Dot, dynamische Farben)

Gemüse Obst Fleisch Fisch Milchprodukte Backwaren
.chip-category[data-color="..."] – Farbe aus DB, Ring-Dot zeigt Selection

### Item Type Chips (Artikel-Typ)

Frisch eingekauft TK-Ware gekauft Selbst eingemacht

### Quick Filter Chips (für Dashboard)

Kühlschrank Gefriertruhe Keller

## edit Inputs

Produktname *
Suchen
Lagerort
Entnommene anzeigen

## inventory_2 Item Cards (Unified)

### Standard Card (Vorrat-Ansicht)

Mangold
500 g · Selbst eingefr.
place Gefriertruhe Gemüse
MHD
30.11.25
Joghurt
4 Stk · Frisch
place Kühlschrank Milchprodukte
Ablauf
in 5 Tagen
Milch
1 L · Frisch
place Kühlschrank Milchprodukte
Ablauf
Morgen

### Card mit Action-Button (Dashboard-Ansicht)

Milch
1 L · Frisch
place Kühlschrank Milchprodukte
Ablauf
in 2 Tagen
Hackfleisch (TK)
500 g · TK gekauft
place Gefriertruhe Fleisch
Ablauf
Abgelaufen

### Item-Type Badges

Frisch TK gekauft Eingefr. Selbst eingefr. Eingemacht
.type-badge.fresh (Leaf) | .type-badge.frozen (Info-Blau) | .type-badge.homemade (Terracotta)
Card-Struktur: Zeile 1: Name | Zeile 2: Menge · Type-Badge | Zeile 3: 📍Lagerort + Kategorie-Tag
Modifiers: .status-warning | .status-critical | .with-action (für Dashboard)

## dashboard Dashboard Components

### Alert Section (mit Unified Cards)

### Bald abgelaufen (3)

Milch
1 L · Frisch
place Kühlschrank Milchprodukte
Ablauf
in 2 Tagen
Joghurt
1 Stk · Frisch
place Kühlschrank Milchprodukte
Ablauf
Morgen
Dashboard verwendet dieselbe .item-card Komponente mit .with-action Modifier

### Statistics Card

bar_chart

### Vorrats-Statistik

24
Artikel
6
Ablauf
3
Entn.

### Summary Box (Wizard)

Zusammenfassung:
Mangold • 500.0 g • Selbst eingefroren • Gemüse Datum: 30.11.2025 • Eingefroren: 30.11.2025

## menu Navigation

### Page Header

arrow_back

# Kategorien

### Bottom Navigation

## settings Admin Components

drag_indicator Gemüse
drag_indicator Obst
drag_indicator Fleisch

## expand_less Bottom Sheet

Mangold
scale
Menge
500 g
place
Lagerort
Gefriertruhe
event
Haltbar bis
30.11.2025
notes
Notizen
blanchiert

## smartphone Fullscreen Demo

# Füllhorn

person admin

### Bald abgelaufen (2)

Milch
1 L · Frisch
place Kühlschrank Milch
Ablauf
in 2 Tagen
Joghurt
1 Stk · Frisch
place Kühlschrank Milch
Ablauf
Morgen
bar_chart

### Vorrats-Statistik

8
Artikel
2
Ablauf
0
Entn.
label

### Schnellfilter

Kühlschrank Keller Gefriertruhe

## code CSS Integration

### Tailwind Config (tailwind.config.js)

    module.exports = {
      theme: {
        extend: {
          colors: {
            // Solarpunk Primary - Greens
            'fern': {
              DEFAULT: '#4A7C59',
              dark: '#3D6A4A',
              light: '#5A8C69',
            },
            'moss': '#5C7F5C',
            'leaf': '#7CB342',
            'sage': {
              DEFAULT: '#9CAF88',
              light: '#B8C9A8',
            },
            // Solarpunk Secondary - Earth & Gold
            'amber': {
              DEFAULT: '#E6A832',
              dark: '#D49A28',
              light: '#F0BC52',
            },
            'honey': '#DAA520',
            'terracotta': '#C17F59',
            // Solarpunk Neutrals
            'cream': '#FAF7F0',
            'oat': '#F5F0E6',
            'parchment': '#EDE8DB',
            'stone': {
              DEFAULT: '#A39E93',
              dark: '#7D796F',
            },
            // Status (override defaults)
            'status-ok': '#7CB342',
            'status-warning': '#E6A832',
            'status-critical': '#E07A5F',
          },
          fontFamily: {
            'display': ['Cormorant Garamond', 'Georgia', 'serif'],
            'body': ['Nunito', 'system-ui', 'sans-serif'],
          },
          borderRadius: {
            'sp-sm': '8px',
            'sp-md': '12px',
            'sp-lg': '16px',
            'sp-xl': '20px',
          },
        },
      },
    }

### NiceGUI Custom CSS (static/solarpunk.css)

    /* ========================================
       FÜLLHORN SOLARPUNK THEME
       NiceGUI / Quasar Override
       ======================================== */

    @import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@500;600;700&family=Nunito:wght@400;500;600;700&display=swap');

    :root {
      /* Primary - Greens */
      --fern: #4A7C59;
      --fern-dark: #3D6A4A;
      --moss: #5C7F5C;
      --leaf: #7CB342;
      --sage: #9CAF88;
      --sage-light: #B8C9A8;

      /* Secondary - Earth & Gold */
      --amber: #E6A832;
      --honey: #DAA520;
      --terracotta: #C17F59;

      /* Neutrals */
      --cream: #FAF7F0;
      --oat: #F5F0E6;
      --parchment: #EDE8DB;
      --stone: #A39E93;
      --charcoal: #3D3D3D;

      /* Status */
      --status-ok: #7CB342;
      --status-warning: #E6A832;
      --status-critical: #E07A5F;

      /* Quasar Primary Override */
      --q-primary: #4A7C59;
      --q-secondary: #E6A832;
      --q-positive: #7CB342;
      --q-negative: #E07A5F;
      --q-warning: #E6A832;
    }

    /* Base */
    body {
      font-family: 'Nunito', system-ui, sans-serif;
      background: var(--cream) !important;
    }

    /* Typography */
    .text-h4, .text-h5, .text-h6 {
      font-family: 'Cormorant Garamond', Georgia, serif !important;
      color: var(--fern) !important;
    }

    /* Cards */
    .q-card {
      background: white !important;
      border-radius: 12px !important;
      box-shadow: 0 2px 8px rgba(74, 124, 89, 0.1) !important;
    }

    /* Buttons */
    .q-btn--standard.bg-primary {
      background: var(--fern) !important;
    }
    .q-btn--standard.bg-primary:hover {
      background: var(--fern-dark) !important;
    }
    .q-btn--outline.text-primary {
      color: var(--fern) !important;
      border-color: var(--fern) !important;
    }
    .q-btn--standard.bg-secondary,
    .q-btn--standard.bg-positive {
      background: var(--amber) !important;
    }
    .q-btn--standard.bg-negative {
      background: var(--status-critical) !important;
    }

    /* Inputs */
    .q-field--outlined .q-field__control {
      border-radius: 12px !important;
    }
    .q-field--outlined .q-field__control:before {
      border-color: var(--sage-light) !important;
    }
    .q-field--outlined.q-field--focused .q-field__control:after {
      border-color: var(--fern) !important;
    }

    /* Bottom Navigation */
    .q-footer, .bottom-nav {
      background: white !important;
      border-top: 1px solid var(--sage-light) !important;
    }
    .q-tab--active {
      color: var(--fern) !important;
    }

    /* Item Cards - Status Border */
    .item-card {
      border-left: 4px solid var(--status-ok);
      border-radius: 12px;
      background: white;
    }
    .item-card.status-warning {
      border-left-color: var(--status-warning);
    }
    .item-card.status-critical {
      border-left-color: var(--status-critical);
    }

    /* Chips Base */
    .solarpunk-chip {
      min-height: 44px;
      padding: 10px 20px;
      border-radius: 9999px;
      font-weight: 500;
      cursor: pointer;
      transition: all 0.2s ease;
    }

    /* Unit Chips */
    .chip-unit {
      background: var(--oat);
      color: var(--charcoal);
      border: 2px solid var(--sage-light);
    }
    .chip-unit:hover {
      border-color: var(--sage);
    }
    .chip-unit.active {
      background: var(--fern);
      color: white;
      border-color: var(--fern);
    }

    /* Ring-Dot for Category/Type Chips */
    .ring-dot {
      width: 18px;
      height: 18px;
      border-radius: 50%;
      border: 2px solid currentColor;
      background: white;
      flex-shrink: 0;
    }
    .ring-dot.filled {
      background: radial-gradient(circle, currentColor 35%, white 35%);
    }
    .chip-category.active .ring-dot,
    .chip-type.active .ring-dot {
      border-color: white;
      background: radial-gradient(circle, white 35%, transparent 35%);
    }

### Farb-Mapping: Alt → Solarpunk

Bisherig (Tailwind) | Solarpunk | Hex
--- | --- | ---
`primary` / `blue-*` | `fern` | #4A7C59
`secondary` | `amber` | #E6A832
`green-500` (OK) | `leaf` / `status-ok` | #7CB342
`orange-500` / `yellow-500` | `status-warning` | #E6A832
`red-500` | `status-critical` | #E07A5F
`gray-100` / `gray-200` | `cream` / `oat` | #FAF7F0
`gray-500` / `gray-600` | `stone` | #A39E93
`border-gray-200` | `sage-light` | #B8C9A8

## integration_instructions NiceGUI Beispiele

### Button (Python)

    # Primary Button
    ui.button('Speichern', icon='save').classes(
        'bg-fern text-white rounded-sp-md'
    )

    # Secondary Button
    ui.button('Zurück', icon='arrow_back').props(
        'flat no-caps'
    ).classes('text-fern')

    # Accent/CTA Button
    ui.button('Weiter', icon='arrow_forward').classes(
        'bg-amber text-white rounded-sp-md'
    )

    # Danger Button
    ui.button('Löschen', icon='delete').classes(
        'bg-status-critical text-white'
    )

### Chip Components (Python)

    # Unit Chip (simplified)
    def unit_chip(label: str, selected: bool = False):
        classes = 'chip-unit active' if selected else 'chip-unit'
        return ui.element('span').classes(classes).text(label)

    # Category Chip (with ring-dot)
    def category_chip(name: str, color: str, selected: bool = False):
        with ui.element('span').classes(
            f'solarpunk-chip chip-category {"active" if selected else ""}'
        ).style(f'--chip-color: {color}'):
            ui.element('span').classes(
                f'ring-dot {"filled" if selected else ""}'
            ).style(f'border-color: {color}')
            ui.label(name)

### Item Card (Python)

    def item_card(item: Item):
        # Status based on days until expiry
        status = 'status-critical' if item.days_left < 3 else \
                 'status-warning' if item.days_left < 7 else ''

        with ui.card().classes(f'item-card {status} w-full mb-2'):
            with ui.row().classes('items-start gap-3'):
                # Status dot
                ui.element('span').classes(
                    'w-3 h-3 rounded-full mt-1'
                ).style(f'background: var(--{status or "status-ok"})')

                # Content
                with ui.column().classes('flex-1 gap-1'):
                    ui.label(item.name).classes('font-semibold')
                    ui.label(f'{item.quantity} {item.unit}').classes(
                        'text-sm text-stone'
                    )
                    with ui.row().classes('items-center gap-1'):
                        ui.icon('place', size='16px').classes('text-terracotta')
                        ui.label(item.location).classes('text-xs text-stone')

                # Expiry
                with ui.column().classes('text-right'):
                    ui.label('Ablauf:').classes('text-xs text-stone')
                    ui.label(item.expiry_text).classes(
                        f'font-semibold text-{status or "status-ok"}'
                    )