# Komponenty GS Loom UI

## GS Loom UI: wspólne działanie, wygląd motywu

GS Loom UI v1 to wspólna warstwa komponentów stron. Jest oddzielna od biblioteki panelu administratora i klienta. Szablon korzysta ze zwykłego HTML z `data-gs-ui`; GS Loom dostarcza bazowy CSS i obsługę interakcji. Motyw określa kolory, zaokrąglenia oraz własne warianty. Dotychczasowy HTML, animacje, schema i uprawnienia pluginów nadal działają.

Ta strona wyjaśnia komponenty za pomocą kodu i przykładów konfiguracji. Paczki motywów zawierają też `snippets/ui-demo.html` do opcjonalnych testów na prywatnej stronie developerskiej.

## Kontrakt i warianty

```html
<button type="button" data-gs-ui="button" data-variant="primary" data-size="md">
  Rozpocznij
</button>
<a href="/contact" data-gs-ui="button" data-variant="outline">Kontakt</a>
```

Do akcji używaj przycisku, do nawigacji linku. Wariant zmienia wygląd, nie semantykę. Rozmiar `data-size` jest niezależny od `data-variant`. Dotychczasowe klasy mogą pozostać obok tych atrybutów. Migracja nie wymaga wymiany wszystkich elementów ani usuwania zaczepów animacji.

| Komponent | Kontrakt HTML | Wbudowane warianty |
| --- | --- | --- |
| Button | `button` lub `a` z `data-gs-ui="button"` | `primary` (domyślny), `secondary`, `outline`, `ghost`, `link`, `danger`; rozmiary `sm`, `md` (domyślny), `lg` |
| Card | `article` lub `div` z `data-gs-ui="card"` | `surface` (domyślny), `outline`, `elevated`, `ghost` |
| Badge | `span` z `data-gs-ui="badge"` | `primary` (domyślny), `secondary`, `outline` |
| Field | wrapper z `data-gs-ui="field"` | wspólne 8 px odstępu etykiety od kontrolki |
| Input / Textarea | `input data-gs-ui="input"` / `textarea data-gs-ui="textarea"` | `outline` (domyślny), `filled`, `underline` |
| Select | `div data-gs-ui="select"` z jednym natywnym `select` | `outline` (domyślny), `filled` |
| Dropdown | `details data-gs-ui="dropdown"`, `summary`, wrapper zawartości | `secondary` (domyślny), `outline`, `primary` |
| Tabs | `div data-gs-ui="tabs"`, lista, przyciski i panele | `line` (domyślny), `pills`; poziome albo pionowe |
| Accordion | `details data-gs-ui="accordion"` z `summary` | `surface` (domyślny), `outline`, `ghost` |
| Modal | `dialog data-gs-ui="modal"` | `default`, `surface`; rozmiary `md` (domyślny), `lg` |

Możesz dodać wariant np. `brand`. Samo nadanie nazwy nie tworzy nowego zachowania — zdefiniuj wygląd w CSS motywu. Wariant odpowiedni dla przycisku nie musi być odpowiedni dla inputu lub modala.

## Zmienne motywu i własne warianty

Zmienne umieść w `assets/ui.css`. GS Loom ładuje wspólne style przed plikami motywu. W szablonie Loom poniższa reguła jest ograniczona do jego obszaru; szablony dokumentowe otrzymują ten sam CSS wewnątrz izolowanego dokumentu.

```css
:scope {
  --gs-ui-accent: #e59d02;
  --gs-ui-on-accent: #161006;
  --gs-ui-text: #f8f8f8;
  --gs-ui-background: #040000;
  --gs-ui-surface: #191715;
  --gs-ui-border: #ffffff29;
  --gs-ui-focus: #e59d02;
  --gs-ui-radius: 20px;
  --gs-ui-button-radius: 50px;
  --gs-ui-shadow: 0 16px 40px #0003;
}
[data-gs-ui="button"][data-variant="brand"] {
  background: #5347ce;
  color: #ffffff;
  border-color: #5347ce;
}
```

Możesz także nadpisać `--gs-ui-danger` i `--gs-ui-on-danger`. Jeśli kolory są edytowalne, połącz zmienne z ustawieniami motywu. Dobieraj kolor tekstu razem z tłem i sprawdzaj kontrast. Skeleton używa żółtego akcentu na ciemnym tle, Paylio ciemnych przycisków na jasnym tle, a Victorie pomarańczowych przycisków oraz jasnych i ciemnych powierzchni. Importowany szablon korzysta z bazowych komponentów platformy; jego plik zmiennych jest częścią eksportowanej paczki.

## Edytowalny wariant przycisku

Dodaj do schema bloku ustawienie `select`, a następnie połącz je z `data-variant`:

```json
{
  "type": "select",
  "id": "button_variant",
  "label": "Wariant przycisku",
  "default": "primary",
  "options": [
    {"value":"primary","label":"Główny"},
    {"value":"secondary","label":"Drugorzędny"},
    {"value":"outline","label":"Obrys"}
  ]
}
```

```html
<a data-gs-ui="button" data-variant="[[ block.settings.button_variant ]]"
   href="[[ safe_url(block.settings.url) ]]"
   data-text-field="[[ block.settings_path ]].label"
   gs-text="block.settings.label"></a>
```

Istniejący blok Card w Skeleton udostępnia już takie ustawienie. Analogicznie możesz udostępnić wariant karty lub innego komponentu. `select` w schema konfiguruje edytor; to co innego niż komponent Select na stronie. Biblioteka UI nie dodaje automatycznie bloku do kreatora — nadal potrzebujesz schema, presetu i dopuszczenia typu przez rodzica.

## Pola i Select

```html
<div data-gs-ui="field">
  <label for="contact-topic">Temat</label>
  <div data-gs-ui="select" data-variant="filled">
    <select id="contact-topic" name="topic" required>
      <option value="">Wybierz temat</option>
      <option value="sales">Sprzedaż</option>
      <option value="support">Pomoc</option>
      <option value="other" disabled>Niedostępne</option>
    </select>
  </div>
</div>
```

Po inicjalizacji platforma pokazuje własny combobox z listą opcji. Natywny select pozostaje źródłem wartości formularza i walidacji. Strzałki przechodzą między dostępnymi opcjami, Home/End wybierają pierwszą/ostatnią, pisanie wyszukuje etykietę, Enter/Spacja zatwierdzają, Escape anuluje, a Tab zamyka listę. Kliknięcie poza kontrolką także ją zamyka. Zdarzenia `change`, `input` i reset formularza zachowują standardowe działanie. Bez JavaScript pozostaje działający natywny select. Wielokrotny wybór nie jest ulepszany w v1.

Zachowaj prawdziwą etykietę połączoną z `id` natywnego selecta; mechanizm przenosi dostępną nazwę na przycisk. Każda instancja potrzebuje unikalnych ID, np. opartych o `block.id` — nie kopiuj identycznych ID do powtarzanych bloków. Input i Textarea umieszczaj w takim samym wrapperze Field, z powiązaną etykietą. Używaj `disabled`, `required` i `aria-invalid` zgodnie z przeznaczeniem. Komponenty nie dostarczają endpointu wysyłki: do wysyłania i walidacji serwerowej używaj pluginu Formularze. Biblioteka nie zastępuje automatycznie pól należących do pluginu ani nie omija uprawnień.

## Dropdown nawigacji i Languages

```html
<details data-gs-ui="dropdown" data-variant="outline">
  <summary>Poznaj nas</summary>
  <nav data-gs-dropdown-content aria-label="Poznaj nas">
    <a href="/about">O nas</a>
    <a href="/contact">Kontakt</a>
  </nav>
</details>
```

Enter/Spacja otwierają natywny element rozwijany. Tab przechodzi między linkami. Escape zamyka i przywraca fokus do summary; opuszczenie kontrolki fokusem lub kliknięcie poza nią również ją zamyka. To rozwijana nawigacja z linkami, a nie ARIA menu. Nie dodawaj ról menu bez odpowiadającej im obsługi klawiatury.

`<gs-languages variant="dropdown"></gs-languages>` korzysta ze wspólnej obsługi dropdownu, zachowując nawigację pluginu językowego i zmianę języka w kreatorze. Każdy szablon obsługujący Languages musi dostarczyć własny wygląd przełącznika. `assets/languages.css` Skeleton rozszerza wspólne zmienne UI. Wariant zwykłych linków nadal jest dostępny. Szczegóły: [Languages](/pl/locales/).

## Tabs i Accordion

```html
<div data-gs-ui="tabs" data-variant="pills" data-value="overview">
  <div data-gs-tab-list aria-label="Informacje o produkcie">
    <button data-gs-tab="overview">Przegląd</button>
    <button data-gs-tab="details">Szczegóły</button>
  </div>
  <section data-gs-panel="overview">Treść przeglądu</section>
  <section data-gs-panel="details">Szczegółowa treść</section>
</div>
<details data-gs-ui="accordion" data-variant="outline">
  <summary>Jak to działa?</summary>
  <p>Twoja odpowiedź.</p>
</details>
```

Klucze zakładek odpowiadają kluczom paneli wewnątrz komponentu. Mechanizm tworzy ID, role i powiązania, ukrywa nieaktywne panele oraz zarządza fokusem klawiatury. Strzałki, Home i End automatycznie wybierają aktywną zakładkę, pomijając wyłączone. `data-orientation="vertical"` korzysta ze strzałek góra/dół. Definicje zakładek i paneli powinny pozostać stałe podczas życia komponentu; przy wymianie całego zestawu zamontuj go ponownie. Przed inicjalizacją JavaScript treść wszystkich paneli pozostaje czytelna.

Accordion korzysta z natywnego działania `details`. Dodaj `open`, aby początkowo rozwinąć element. Wspólna wartość `name` na powiązanych details pozwala utworzyć grupę z jednym otwartym elementem; nazwa grupy powinna być unikalna dla instancji bloku.

## Modal

```html
<button type="button" data-gs-ui="button" data-gs-open="shipping-dialog">Dostawa</button>
<dialog data-gs-ui="modal" data-size="lg" id="shipping-dialog" aria-labelledby="shipping-title">
  <h2 id="shipping-title">Informacje o dostawie</h2>
  <p>Treść wyświetlana na żądanie.</p>
  <button type="button" data-gs-ui="button" data-variant="secondary" data-gs-close autofocus>Zamknij</button>
</dialog>
```

Mechanizm otwiera natywny `dialog` przez `showModal()`: fokus pozostaje w modalu, Escape zamyka, a fokus wraca do wywołującego elementu. Zamyka także przycisk i kliknięcie poza prostokątem dialogu. Dodaj dostępną nazwę oraz widoczny przycisk zamykania. Cel jest wyszukiwany wewnątrz wrappera danego motywu; powtarzane bloki potrzebują unikalnych ID. Modal sam w sobie nie jest systemem uprawnień ani zatwierdzania i niczego nie wysyła.

## Focus, active i stany nieaktywne

Fokus pozostaje widoczny bez zmiany kształtu komponentu. `:active` oznacza naciskanie; trwały wybór oznaczają `aria-selected`, `aria-current` lub `details[open]`. Natywne wyłączone przyciski nie uruchamiają akcji. Dla nieaktywnego linku użyj `aria-disabled="true"` i `tabindex="-1"`; wspólny mechanizm blokuje aktywację. Klasa wyglądająca na nieaktywną nie zastępuje kontroli dostępu. Zobacz [zasady fokusu](/pl/inputs/) dla wyszukiwarek i dropdownów.

## Migracja i sprawdzenie

Trzy dołączone motywy zawierają zmienne UI i snippet demonstracyjny. Dotychczasowe przyciski i karty Skeleton/Paylio oraz przyciski CTA Victorie korzystają ze wspólnego kontraktu, zachowując oryginalne klasy i zaczepy animacji. Wyspecjalizowana nawigacja, slidery i odtwarzacze wideo zachowują dotychczasowe działanie — nie są automatycznie zastępowane ogólnymi komponentami.

Sprawdź warianty i motywy, klawiaturę, etykiety, walidację, wyłączone opcje, reset formularza, Escape i powrót fokusu modala, dwie instancje na stronie, mobile i ograniczenie animacji. Zmiany CSS i działania UI należą do motywu/mechanizmu platformy, nie do zapisanej treści klienta. Przebuduj przez `npm run loom:build`; paczkę na portalu deweloperskim odświeża `npm run build:developers`. Nie kopiuj kodu platformy do każdej paczki — GS Loom dostarcza go w renderowaniu i podglądzie.

## Równa wysokość i grow

Dodaj `data-gs-equal-height` do wiersza flex lub siatki grid. Każde bezpośrednie dziecko staje się kontenerem flex; karta lub przycisk wewnątrz niego otrzymuje `data-gs-grow`. Elementy rozciągają się do wysokości najwyższej treści w swoim wierszu, także po tłumaczeniu lub edycji na podglądzie. Szablon nadal definiuje kolumny, odstępy i breakpointy. Nie ustawiaj stałych wysokości ani pomiarów JavaScript.

```html
<div data-gs-equal-height class="feature-grid">
  <div>
    <article data-gs-ui="card" data-gs-grow data-gs-stack>
      <h3>Feature</h3>
      <p>Short or long translated content.</p>
      <div data-gs-footer>
        <a data-gs-ui="button" href="/details">Details</a>
      </div>
    </article>
  </div>
  <!-- Repeat the wrapper for each card. -->
</div>
```

```css
.feature-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
  gap: 24px;
}
```

`data-gs-stack` tworzy pionowy układ flex; `data-gs-footer` przesuwa akcje na dół. Zachowaj odstęp nad akcjami (np. gap lub padding stopki). `data-gs-grow` działa także samodzielnie wewnątrz układów flex. Na telefonie osobne wiersze zachowują naturalne wysokości. Jeśli WSZYSTKIE wiersze grid mają być równe, dodaj świadomie `grid-auto-rows: 1fr` do siatki. Atrybuty współpracują z każdym wariantem komponentu i nie zastępują jego kolorów, obramowania ani animacji.

## Nagłówek i menu w kreatorze

Wspólny kontrakt GS Loom: `data-gs-header="header"` oznacza nagłówek, `data-gs-logo` logo, a `data-gs-menu="header"` lub `"footer"` nawigację. `<gs-menu location="header">` dodaje oznaczenie automatycznie i korzysta z menu witryny. Zachowaj semantyczne `header`, `nav` i linki. Szablon odpowiada za wygląd, breakpointy oraz dropdowny.

Jeżeli nagłówek ma własne pola schematu, dodaj `data-gs-header-group="header"`. Kliknięcie nagłówka, logo lub pozycji otwiera ustawienia tej grupy w kreatorze; zmiany zapisują się w `loom.groups.header` (historyczna nazwa pola danych GS Loom). Skeleton korzysta z tego wariantu, aby zachować istniejące zagnieżdżone menu i tłumaczenia. Bez tego atrybutu logo używa wspólnego edytora logo, a menu wspólnego menedżera nawigacji. Nie łącz obu źródeł danych dla tych samych pozycji. Publiczne linki zachowują normalne działanie.

## Dropdown, zagnieżdżenia i megamenu

GS Loom rozdziela dane menu od wyglądu. Kreator zapisuje drzewo pozycji, `<gs-menu>` renderuje to drzewo, a CSS szablonu określa wygląd. Poniżej znajdują się przykłady kodu, a nie interaktywne podglądy.

### 1. Umieść menu w nagłówku

```html
<gs-menu location="header" label="Main navigation"></gs-menu>
```

Ten kod umieść w sekcji lub snippecie nagłówka szablonu. `location="header"` wybiera menu witryny przypisane do tej lokalizacji, a `label` jest nazwą dla czytników ekranu. Nie wklejaj poniższego JSON do HTML — pokazuje on dane pozycji zapisywane w menu witryny. W kreatorze użyj **Nawigacja → Header**, rozwiń pozycję i wybierz **Dodaj pozycję podrzędną**.

Edycja menu nagłówka włącza `use_site_menu`. Paylio korzysta ze wspólnego nagłówka; Skeleton i Victorie Vending zachowują oryginalną nawigację demonstracyjną do czasu włączenia tego ustawienia. Dla danego menu używaj jednego źródła danych, zamiast równolegle utrzymywać linki wpisane w HTML i pozycje menu witryny.

### 2. Dropdown z dziećmi i kolejnym poziomem

```json
{
  "id": "services",
  "label": "Services",
  "href": "/services",
  "menu_layout": "dropdown",
  "children": [
    {
      "id": "websites",
      "label": "Websites",
      "href": "/websites",
      "icon": "globe",
      "children": [
        {
          "id": "design",
          "label": "Design",
          "href": "/design",
          "icon": "star"
        },
        {
          "id": "development",
          "label": "Development",
          "href": "/development",
          "icon": "code"
        }
      ]
    }
  ]
}
```

`Services → Websites → Design / Development` tworzy trzy poziomy. Każdy obiekt w `children` ma ten sam format, więc dziecko może mieć własne dzieci. Pozycja bez dzieci jest zwykłym linkiem. Brak `menu_layout` oznacza `dropdown`. Ten obiekt należy do tablicy `items` menu nagłówka.

| Pole | Znaczenie |
| --- | --- |
| `id` | Stały identyfikator pozycji; zachowaj go przy zmianie nazwy lub kolejności. |
| `label` | Widoczny tekst linku lub przycisku otwierającego podmenu. |
| `href` | Adres docelowy. Użyj `#`, gdy rodzic ma wyłącznie otwierać podmenu. |
| `children` | Tablica dzieci; pomiń dla zwykłego linku. |
| `menu_layout` | `dropdown` tworzy listę, `mega` układa bezpośrednie dzieci w kolumnach. |
| `icon` | Klucz ikony bibliotecznej lub `image:<URL>`; pomiń, jeśli nie chcesz ikony. |

### 3. Megamenu z ikonami

```json
{
  "id": "explore",
  "label": "Explore",
  "href": "#",
  "menu_layout": "mega",
  "children": [
    {
      "id": "products",
      "label": "Products",
      "href": "/products",
      "icon": "star",
      "children": [
        {
          "id": "skeleton",
          "label": "Skeleton",
          "href": "/products/skeleton"
        },
        {
          "id": "paylio",
          "label": "Paylio",
          "href": "/products/paylio"
        }
      ]
    },
    {
      "id": "support",
      "label": "Support",
      "href": "/support",
      "icon": "image:/media/support.svg",
      "children": [
        {
          "id": "contact",
          "label": "Contact",
          "href": "/contact"
        },
        {
          "id": "docs",
          "label": "Documentation",
          "href": "/docs"
        }
      ]
    }
  ]
}
```

Ustaw `menu_layout: "mega"` na rodzicu otwierającym szeroki panel. Tutaj Products i Support tworzą dwie kolumny, a ich `children` zawierają linki pod nagłówkami. `star` wybiera ikonę biblioteczną. `image:/media/support.svg` pokazuje własny obraz — zastąp tę ścieżkę rzeczywistym adresem z menedżera plików. Pole przyjmuje adres obrazu, a nie surowy kod SVG. Zawartość kolumn zmieniasz w danych bez przebudowy renderera.

### 4. Dopasuj wygląd do szablonu

```css
[data-gs-menu="header"] {
  --gs-menu-background: #17201c;
  --gs-menu-color: #ffffff;
  --gs-menu-border: #ffffff33;
  --gs-menu-radius: 16px;
  --gs-menu-gap: 24px;
}
```

Dodaj te reguły do arkusza stylów szablonu. Selektor ogranicza tokeny do menu nagłówka. Tło, tekst, obramowanie, promień narożników i odstępy mogą być inne w każdym szablonie przy zachowaniu wspólnego działania. Zachowaj widoczny focus i dopasuj panele do szerokości ekranu.

### 5. Zachowanie, tłumaczenia i limity

Kliknięcie lub Enter/Spacja otwiera podmenu. Escape zamyka bieżący poziom i przywraca focus na jego przycisk. Jeżeli rodzic ma rzeczywisty URL, renderer dodaje jego link na początku panelu — samo otwarcie nie przenosi użytkownika na inną stronę. Na małych ekranach zagnieżdżenia rozwijają się pionowo. Megamenu nie jest osobną stroną ani pluginem.

Przy włączonym Languages edytuj etykiety dla poszczególnych języków w kreatorze. Zachowuj identyfikatory pozycji, aby tłumaczenia nadal dotyczyły właściwych elementów; ikony, adresy i układ są niezależne od języka. Limity: **5 poziomów łącznie z głównym**, **30 pozycji w jednej gałęzi** i **100 pozycji w menu**. Sprawdź długie tłumaczenia, obsługę klawiatury i układ mobilny. Szczegóły danych znajdziesz w [Menu](/pl/menus/), a lokalizacji w [Językach](/pl/locales/).


### Wizualna edycja menu w kreatorze

Kliknij nagłówek Skeleton, aby otworzyć drzewo menu. Lista zawiera istniejące pozycje; rozwiń rodzica, aby zobaczyć dzieci. Wybierz pozycję i kliknij jej nazwę, aby edytować ją bezpośrednio. Link, ikona i układ podmenu są pokazywane tylko dla wybranej pozycji. Dodawaj, usuwaj i zmieniaj kolejność bez edycji pól schematu. Zakładka Wygląd zawiera kolory tekstu, tła i aktywnej pozycji, czcionkę, rozmiar oraz odstępy. Zastosowanie zapisuje szkic i zachowuje tłumaczenia; publikacja jest osobną czynnością.
