/ Gists

Gists - popular

On gists

PHP OOP Tricks & Tips

Popular ⭐ PHP

readme.md #

<?php
/**
 * ========================================
 * PHP/OOP Best Practices & Patterns
 * ========================================
 */

// ============================================================================
// 1. ✅ KONSTANTY MÍSTO "MAGIC STRINGS"
// ============================================================================

// ❌ ŠPATNĚ - Magic strings, překlepy nenajdeš
if ($order->state === 'pending') { }
if ($order->state === 'pendig') { } // Překlep!

// ✅ DOBŘE - Konstanty, IDE ti pomůže
class OrderState 
{
    const PENDING = 'pending';
    const SHIPPED = 'shipped';
    const DELIVERED = 'delivered';
}

if ($order->state === OrderState::PENDING) { }
if ($order->state === OrderState::PENDIG) { } // IDE error!

// ============================================================================
// 2. 🎯 ENUM PATTERN (PHP 7.4) - Rozšířené konstanty
// ============================================================================

class OrderStateEnum 
{
    const PENDING = 1;
    const SHIPPED = 2;
    const DELIVERED = 3;

    private static $names = [
        self::PENDING => 'Čeká na zpracování',
        self::SHIPPED => 'Odesláno',
        self::DELIVERED => 'Doručeno',
    ];

    private static $colors = [
        self::PENDING => 'orange',
        self::SHIPPED => 'blue',
        self::DELIVERED => 'green',
    ];

    public static function getName(int $state): string
    {
        return self::$names[$state] ?? 'Neznámý stav';
    }

    public static function getColor(int $state): string
    {
        return self::$colors[$state] ?? 'gray';
    }

    public static function getAll(): array
    {
        return self::$names;
    }
}

// Použití:
$stateName = OrderStateEnum::getName(OrderStateEnum::PENDING); // "Čeká na zpracování"
$color = OrderStateEnum::getColor(OrderStateEnum::PENDING); // "orange"

// ============================================================================
// 3. 🚀 VALUE OBJECTS - Type Safety & Zapouzdření Logiky
// ============================================================================

// PŘÍKLAD 1: Money - Nemůžeš sčítat různé měny
// ❌ ŠPATNĚ
function calculateTotal(float $priceEur, float $priceCzk): float 
{
    return $priceEur + $priceCzk; // Sčítáš EUR + CZK?? 😱
}
$total = calculateTotal(100, 2500); // 2600 čeho??

// ✅ DOBŘE - Value Object
class Money 
{
    private float $amount;
    private string $currency;

    public function __construct(float $amount, string $currency) 
    {
        $this->amount = $amount;
        $this->currency = $currency;
    }

    public function add(Money $other): Money 
    {
        if ($this->currency !== $other->currency) {
            throw new \InvalidArgumentException(
                "Cannot add {$other->currency} to {$this->currency}"
            );
        }
        return new Money($this->amount + $other->amount, $this->currency);
    }

    public function multiply(float $multiplier): Money 
    {
        return new Money($this->amount * $multiplier, $this->currency);
    }

    public function getAmount(): float { return $this->amount; }
    public function getCurrency(): string { return $this->currency; }
}

// Použití:
$priceEur = new Money(100, 'EUR');
$priceCzk = new Money(2500, 'CZK');
$total = $priceEur->add($priceCzk); // ❌ Exception! Nelze EUR + CZK
$discount = $priceEur->multiply(0.9); // ✅ 90 EUR

// PŘÍKLAD 2: Percentage - Validace 0-100
// ❌ ŠPATNĚ
function applyDiscount(float $price, int $percent): float 
{
    return $price - ($price * $percent / 100);
}
$final = applyDiscount(1000, 150); // 150% sleva?? -500 Kč? 😱

// ✅ DOBŘE - Value Object
class Percentage 
{
    private int $value;

    public function __construct(int $value) 
    {
        if ($value < 0 || $value > 100) {
            throw new \InvalidArgumentException(
                "Percentage must be 0-100, got: $value"
            );
        }
        $this->value = $value;
    }

    public function getValue(): int { return $this->value; }

    public function applyTo(float $amount): float 
    {
        return $amount * ($this->value / 100);
    }
}

function applyDiscountSafe(float $price, Percentage $percent): float 
{
    return $price - $percent->applyTo($price);
}

$final = applyDiscountSafe(1000, new Percentage(150)); // ❌ Exception!
$final = applyDiscountSafe(1000, new Percentage(20));  // ✅ 800 Kč

// PŘÍKLAD 3: OrderState Value Object - Jen platné stavy
class OrderStateVO 
{
    private int $value;

    private const NEW = 1;
    private const PAID = 2;
    private const SHIPPED = 3;

    private static $valid = [self::NEW, self::PAID, self::SHIPPED];

    private function __construct(int $value) 
    {
        if (!in_array($value, self::$valid)) {
            throw new \InvalidArgumentException("Invalid state: $value");
        }
        $this->value = $value;
    }

    public static function new(): self { return new self(self::NEW); }
    public static function paid(): self { return new self(self::PAID); }
    public static function shipped(): self { return new self(self::SHIPPED); }

    public function isNew(): bool { return $this->value === self::NEW; }
    public function isPaid(): bool { return $this->value === self::PAID; }

    public function getValue(): int { return $this->value; }
}

// Použití:
$state = OrderStateVO::paid(); // ✅ Jen platné stavy!
$state = new OrderStateVO(999); // ❌ Private constructor - nelze!
if ($state->isPaid()) { /* Ship order */ }

// ============================================================================
// 4. 🛡️ NULL OBJECT PATTERN - Žádné null checks
// ============================================================================

// ❌ ŠPATNĚ - Null checks všude
$user = $userRepo->find($id);
if ($user !== null) {
    echo $user->getName();
} else {
    echo 'Guest';
}

// ✅ DOBŘE - Null Object
interface UserInterface 
{
    public function getName(): string;
    public function isGuest(): bool;
}

class User implements UserInterface 
{
    private string $name;

    public function __construct(string $name) 
    {
        $this->name = $name;
    }

    public function getName(): string { return $this->name; }
    public function isGuest(): bool { return false; }
}

class GuestUser implements UserInterface 
{
    public function getName(): string { return 'Guest'; }
    public function isGuest(): bool { return true; }
}

// Použití:
$user = $userRepo->find($id) ?? new GuestUser();
echo $user->getName(); // Vždy funguje, žádný null check!

// ============================================================================
// 5. 🎨 EARLY RETURN - Guard Clauses místo vnořených if
// ============================================================================

// ❌ ŠPATNĚ - Pyramid of Doom
function processOrder($data) 
{
    if ($data !== null) {
        if ($data->isValid()) {
            if ($data->hasPermission()) {
                return $data->process();
            } else {
                return 'No permission';
            }
        } else {
            return 'Invalid';
        }
    } else {
        return 'No data';
    }
}

// ✅ DOBŘE - Guard Clauses (Early Return)
function processOrderClean($data) 
{
    if ($data === null) {
        return 'No data';
    }

    if (!$data->isValid()) {
        return 'Invalid';
    }

    if (!$data->hasPermission()) {
        return 'No permission';
    }

    return $data->process();
}

// ============================================================================
// 6. 🔒 IMMUTABLE OBJECTS - Objekty se nemění
// ============================================================================

// ❌ ŠPATNĚ - Mutable (někdo změní a nevíš kde)
class MutablePrice 
{
    public float $value;

    public function __construct(float $value) 
    {
        $this->value = $value;
    }

    public function setValue(float $value) 
    {
        $this->value = $value;
    }
}

$price = new MutablePrice(100);
someFunction($price); // Co se stalo uvnitř?
echo $price->value; // Kdo ví... možná 100, možná 200?

// ✅ DOBŘE - Immutable (vrací nové instance)
class ImmutablePrice 
{
    private float $value;

    public function __construct(float $value) 
    {
        $this->value = $value;
    }

    public function getValue(): float { return $this->value; }

    public function withDiscount(int $percent): self 
    {
        return new self($this->value * (100 - $percent) / 100);
    }

    public function withTax(float $taxRate): self 
    {
        return new self($this->value * (1 + $taxRate));
    }
}

// Použití:
$price = new ImmutablePrice(100);
$discounted = $price->withDiscount(10); // Nový objekt
$withTax = $discounted->withTax(0.21); // Další nový objekt

echo $price->getValue(); // Stále 100! Původní nezměněn
echo $discounted->getValue(); // 90
echo $withTax->getValue(); // 108.9

// ============================================================================
// 7. 🎯 TYPE HINTS VŠUDE (PHP 7.4)
// ============================================================================

// ❌ ŠPATNĚ - Bez type hints
function calculate($a, $b) 
{
    return $a + $b;
}
calculate('10', '20'); // "1020" (string concatenation) 😱
calculate([1, 2], [3, 4]); // Fatal error až za běhu

// ✅ DOBŘE - S type hints
function calculateSafe(int $a, int $b): int 
{
    return $a + $b;
}
calculateSafe('10', '20'); // TypeError hned! ✅
calculateSafe(10, 20); // 30 ✅

// ============================================================================
// 8. 📦 DEPENDENCY INJECTION místo new
// ============================================================================

// ❌ ŠPATNĚ - Tight Coupling (pevné závislosti)
class OrderService 
{
    public function process() 
    {
        $mailer = new Mailer(); // ❌ Nelze testovat, nelze změnit
        $logger = new Logger(); // ❌ Pevně spojené třídy

        $mailer->send('order@email.com', 'Order processed');
        $logger->log('Order processed');
    }
}

// ✅ DOBŘE - Dependency Injection
class OrderServiceWithDI 
{
    private Mailer $mailer;
    private Logger $logger;

    public function __construct(Mailer $mailer, Logger $logger) 
    {
        $this->mailer = $mailer;
        $this->logger = $logger;
    }

    public function process() 
    {
        $this->mailer->send('order@email.com', 'Order processed');
        $this->logger->log('Order processed');
    }
}

// Použití - lze snadno měnit implementace, testovat s mocky
$service = new OrderServiceWithDI(
    new SmtpMailer(), // nebo MockMailer() v testech
    new FileLogger()  // nebo DatabaseLogger()
);

// ============================================================================
// 9. 🔥 SINGLE RESPONSIBILITY - Jedna třída = jeden důvod ke změně
// ============================================================================

// ❌ ŠPATNĚ - Dělá všechno
class OrderGod 
{
    public function createOrder($data) { /* ... */ }
    public function sendEmail($to, $subject) { /* ... */ }
    public function logToDatabase($message) { /* ... */ }
    public function validateCreditCard($number) { /* ... */ }
    public function generatePdf($order) { /* ... */ }
}

// ✅ DOBŘE - Každá třída má jednu odpovědnost
class OrderCreator 
{
    public function create($data) { /* ... */ }
}

class EmailSender 
{
    public function send($to, $subject, $body) { /* ... */ }
}

class Logger 
{
    public function log($message) { /* ... */ }
}

class CreditCardValidator 
{
    public function validate($number): bool { /* ... */ }
}

class PdfGenerator 
{
    public function generate($order): string { /* ... */ }
}

// ============================================================================
// 10. 🎭 INTERFACE SEGREGATION - Malé, specifické interfaces
// ============================================================================

// ❌ ŠPATNĚ - Obrovský interface, vynucuje nepotřebné metody
interface WorkerGod 
{
    public function work();
    public function eat();
    public function sleep();
    public function getPaid();
}

class Robot implements WorkerGod 
{
    public function work() { /* OK */ }
    public function eat() { /* Robot nejí! */ }
    public function sleep() { /* Robot nespí! */ }
    public function getPaid() { /* Robot nedostává výplatu! */ }
}

// ✅ DOBŘE - Malé, specifické interfaces
interface Workable 
{
    public function work();
}

interface Eatable 
{
    public function eat();
}

interface Sleepable 
{
    public function sleep();
}

interface Payable 
{
    public function getPaid();
}

class Human implements Workable, Eatable, Sleepable, Payable 
{
    public function work() { /* ... */ }
    public function eat() { /* ... */ }
    public function sleep() { /* ... */ }
    public function getPaid() { /* ... */ }
}

class RobotWorker implements Workable 
{
    public function work() { /* ... */ }
    // Jen to, co skutečně umí!
}

// ============================================================================
// 11. 🏭 FACTORY PATTERN - Vytváření komplexních objektů
// ============================================================================

// ❌ ŠPATNĚ - Složité vytváření všude v kódu
$order = new Order();
$order->setCustomer($customer);
$order->setShippingAddress($address);
$order->setPaymentMethod($payment);
$order->setState(OrderState::NEW);
$order->setCreatedAt(new DateTime());
// ... 20 dalších setterů

// ✅ DOBŘE - Factory zapouzdřuje logiku vytváření
class OrderFactory 
{
    public function createFromCart(Cart $cart, Customer $customer): Order 
    {
        $order = new Order();
        $order->setCustomer($customer);
        $order->setShippingAddress($customer->getDefaultAddress());
        $order->setPaymentMethod($cart->getPaymentMethod());
        $order->setState(OrderState::NEW);
        $order->setCreatedAt(new DateTime());
        $order->setTotal($cart->getTotal());

        foreach ($cart->getItems() as $item) {
            $order->addItem($item);
        }

        return $order;
    }
}

// Použití:
$orderFactory = new OrderFactory();
$order = $orderFactory->createFromCart($cart, $customer);

// ============================================================================
// 12. 🔗 METHOD CHAINING (Fluent Interface)
// ============================================================================

// ❌ ŠPATNĚ - Hodně řádků
$query = new QueryBuilder();
$query->select('*');
$query->from('users');
$query->where('active', true);
$query->orderBy('created_at', 'DESC');
$query->limit(10);
$result = $query->get();

// ✅ DOBŘE - Fluent interface (vrací $this)
class QueryBuilder 
{
    private string $select = '';
    private string $from = '';
    private array $where = [];

    public function select(string $columns): self 
    {
        $this->select = $columns;
        return $this; // Vrací sebe sama
    }

    public function from(string $table): self 
    {
        $this->from = $table;
        return $this;
    }

    public function where(string $column, $value): self 
    {
        $this->where[] = [$column, $value];
        return $this;
    }

    public function get(): array 
    {
        // Execute query
        return [];
    }
}

// Použití - řetězení metod:
$result = (new QueryBuilder())
    ->select('*')
    ->from('users')
    ->where('active', true)
    ->orderBy('created_at', 'DESC')
    ->limit(10)
    ->get();

// ============================================================================
// 13. 💎 BONUS: Praktické tipy
// ============================================================================

// TIP 1: Používej ?? místo ternary pro null checks
$name = $user->name ?? 'Guest'; // místo: $user->name ? $user->name : 'Guest'

// TIP 2: Spaceship operator pro porovnání
usort($array, fn($a, $b) => $a <=> $b); // místo: $a > $b ? 1 : ($a < $b ? -1 : 0)

// TIP 3: Null safe operator (PHP 8.0+, ale dobrý vědět)
// $country = $user?->getAddress()?->getCountry()?->getName();

// TIP 4: Array destructuring
[$firstName, $lastName] = explode(' ', $fullName);

// TIP 5: Arrow functions (PHP 7.4+)
$prices = array_map(fn($item) => $item->price, $items);

// TIP 6: Typed properties (PHP 7.4+)
class Product 
{
    private int $id;
    private string $name;
    private float $price;
    private ?string $description = null; // Nullable
}

// TIP 7: Spread operator
$array1 = [1, 2, 3];
$array2 = [...$array1, 4, 5, 6]; // [1, 2, 3, 4, 5, 6]

// TIP 8: Named arguments (PHP 8.0+, ale dobrý vědět)
// createOrder(customer: $customer, total: 1000, shipping: 'DHL');

// ============================================================================
// 📚 SHRNUTÍ - Co používat VŽDY:
// ============================================================================

/*
1. ✅ Konstanty místo magic strings/numbers
2. ✅ Type hints všude (int, string, array, vlastní třídy)
3. ✅ Early return (guard clauses)
4. ✅ Dependency Injection
5. ✅ Single Responsibility (malé, zaměřené třídy)
6. ✅ Value Objects pro důležitá data (Money, Email, Percentage)
7. ✅ Immutabilita kde to dává smysl
8. ✅ Null Object pattern místo null checks
9. ✅ PHPDoc kde type hints nestačí
10. ✅ Smysluplné názvy (ne $a, $tmp, $data)

❌ NIKDY:
- Magic strings/numbers bez konstant
- Velké třídy (God Objects)
- new v business logice (použij DI)
- Public properties (použij gettery/settery)
- Hluboké vnořené ify (pyramid of doom)
*/

Máš to! Celé najednou pro Gist. Chceš ještě něco konkrétního? 🚀


On gists

Tailwind advanced

Popular ⭐ Tailwind CSS

index.html #

<!DOCTYPE html>
<html lang="cs">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Tailwind CSS - Advanced Triky</title>
  <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="p-8 bg-gray-50">
  <h1 class="text-3xl font-bold mb-8">Tailwind CSS - Advanced Triky 🚀</h1>

  <!-- ============================================ -->
  <!-- #2 PEER SELEKTORY -->
  <!-- ============================================ -->
  <section class="mb-12 bg-white p-6 rounded-lg border-2 border-blue-200">
    <h2 class="text-2xl font-bold mb-4 text-blue-700">#2 Peer selektory</h2>
    <p class="text-gray-600 mb-4">Sourozenec ovlivňuje sourozence</p>
    <div class="bg-gray-50 p-4 rounded mb-4">
      <code class="text-sm">peer + peer-checked:class</code>
    </div>
    
    <div class="mb-6">
      <h3 class="font-semibold mb-3">Checkbox ovládá label</h3>
      <div class="flex items-center gap-3">
        <input type="checkbox" id="check1" class="peer w-5 h-5" />
        <label for="check1" class="peer-checked:text-blue-600 peer-checked:font-bold cursor-pointer">
          Zaškrtni checkbox → label se změní
        </label>
      </div>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Named peers (více peer prvků)</h3>
      <div class="space-y-3">
        <div>
          <input type="radio" name="opt" id="opt1" class="peer/opt1 hidden" />
          <label for="opt1" class="block p-3 border-2 border-gray-300 rounded cursor-pointer peer-checked/opt1:border-green-500 peer-checked/opt1:bg-green-50">
            Varianta 1
          </label>
          <div class="hidden peer-checked/opt1:block mt-2 p-3 bg-green-100 rounded">
            ✅ Obsah pro variantu 1
          </div>
        </div>
        <div>
          <input type="radio" name="opt" id="opt2" class="peer/opt2 hidden" />
          <label for="opt2" class="block p-3 border-2 border-gray-300 rounded cursor-pointer peer-checked/opt2:border-purple-500 peer-checked/opt2:bg-purple-50">
            Varianta 2
          </label>
          <div class="hidden peer-checked/opt2:block mt-2 p-3 bg-purple-100 rounded">
            ✅ Obsah pro variantu 2
          </div>
        </div>
      </div>
    </div>
  </section>

  <!-- ============================================ -->
  <!-- #4 MULTIPLE CONDITIONS -->
  <!-- ============================================ -->
  <section class="mb-12 bg-white p-6 rounded-lg border-2 border-green-200">
    <h2 class="text-2xl font-bold mb-4 text-green-700">#4 Multiple conditions (AND)</h2>
    <p class="text-gray-600 mb-4">Několik podmínek musí platit zároveň</p>
    <div class="bg-gray-50 p-4 rounded mb-4">
      <code class="text-sm">data-[active]:data-[type=premium]:bg-gold</code>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Obě podmínky musí platit</h3>
      <div data-active data-type="premium" class="p-4 border-2 border-gray-300 data-[active]:data-[type=premium]:bg-yellow-100 data-[active]:data-[type=premium]:border-yellow-500 data-[active]:data-[type=premium]:font-bold">
        ✅ data-active + data-type="premium" → žluté pozadí
      </div>
      <div data-active class="p-4 mt-2 border-2 border-gray-300 data-[active]:data-[type=premium]:bg-yellow-100 data-[active]:data-[type=premium]:border-yellow-500">
        ❌ Jen data-active (chybí premium) → normální
      </div>
      <div data-type="premium" class="p-4 mt-2 border-2 border-gray-300 data-[active]:data-[type=premium]:bg-yellow-100 data-[active]:data-[type=premium]:border-yellow-500">
        ❌ Jen data-type="premium" (chybí active) → normální
      </div>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Kombinace s hover a data atributem</h3>
      <button data-variant="danger" class="p-3 bg-red-500 text-white rounded hover:data-[variant=danger]:bg-red-700 hover:data-[variant=danger]:scale-105 transition">
        Hover + data-variant="danger" → tmavší a větší
      </button>
    </div>
  </section>

  <!-- ============================================ -->
  <!-- #5 NTH-CHILD SELEKTORY -->
  <!-- ============================================ -->
  <section class="mb-12 bg-white p-6 rounded-lg border-2 border-purple-200">
    <h2 class="text-2xl font-bold mb-4 text-purple-700">#5 Nth-child selektory</h2>
    <p class="text-gray-600 mb-4">Stylování podle pozice v DOM</p>
    <div class="bg-gray-50 p-4 rounded mb-4">
      <code class="text-sm">[&>*:nth-child(odd)]:bg-gray-100</code>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Liché/sudé řádky (zebra pattern)</h3>
      <div class="[&>*:nth-child(odd)]:bg-gray-100 [&>*:nth-child(even)]:bg-white border-2 border-gray-300 rounded overflow-hidden">
        <div class="p-3">Řádek 1 (lichý) - šedé pozadí</div>
        <div class="p-3">Řádek 2 (sudý) - bílé pozadí</div>
        <div class="p-3">Řádek 3 (lichý) - šedé pozadí</div>
        <div class="p-3">Řádek 4 (sudý) - bílé pozadí</div>
      </div>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">První a poslední prvek</h3>
      <div class="[&>*:first-child]:font-bold [&>*:first-child]:text-green-600 [&>*:last-child]:font-bold [&>*:last-child]:text-red-600 space-y-2">
        <div class="p-3 border rounded">První prvek - zelený tučný</div>
        <div class="p-3 border rounded">Prostřední prvek - normální</div>
        <div class="p-3 border rounded">Poslední prvek - červený tučný</div>
      </div>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Každý třetí prvek</h3>
      <div class="[&>*:nth-child(3n)]:bg-blue-100 [&>*:nth-child(3n)]:border-blue-500 space-y-2">
        <div class="p-2 border-2 rounded">Prvek 1</div>
        <div class="p-2 border-2 rounded">Prvek 2</div>
        <div class="p-2 border-2 rounded">Prvek 3 - modrý (3n)</div>
        <div class="p-2 border-2 rounded">Prvek 4</div>
        <div class="p-2 border-2 rounded">Prvek 5</div>
        <div class="p-2 border-2 rounded">Prvek 6 - modrý (3n)</div>
      </div>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Not last-child (margin mezi prvky)</h3>
      <div class="[&>*:not(:last-child)]:mb-4">
        <div class="p-3 bg-gray-100 rounded">Prvek s marginem dole</div>
        <div class="p-3 bg-gray-100 rounded">Prvek s marginem dole</div>
        <div class="p-3 bg-gray-100 rounded">Poslední prvek BEZ marginu</div>
      </div>
    </div>
  </section>

  <!-- ============================================ -->
  <!-- #6 BEFORE/AFTER -->
  <!-- ============================================ -->
  <section class="mb-12 bg-white p-6 rounded-lg border-2 border-orange-200">
    <h2 class="text-2xl font-bold mb-4 text-orange-700">#6 Before/After pseudo-elementy</h2>
    <p class="text-gray-600 mb-4">Přidání obsahu bez HTML</p>
    <div class="bg-gray-50 p-4 rounded mb-4">
      <code class="text-sm">before:content-['→'] after:content-['✓']</code>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Ikony před/za textem</h3>
      <div class="before:content-['→'] before:mr-2 before:text-blue-500 p-3 border rounded">
        Text se šipkou před
      </div>
      <div class="after:content-['✓'] after:ml-2 after:text-green-500 p-3 border rounded mt-2">
        Text se zaškrtávátkem za
      </div>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Dekorativní čára</h3>
      <h2 class="text-xl font-bold before:content-[''] before:inline-block before:w-12 before:h-1 before:bg-purple-500 before:mr-3 before:align-middle">
        Nadpis s čárou
      </h2>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Badge/štítek</h3>
      <div class="relative inline-block">
        <button class="px-4 py-2 bg-blue-500 text-white rounded after:content-['3'] after:absolute after:-top-2 after:-right-2 after:bg-red-500 after:text-white after:rounded-full after:w-6 after:h-6 after:flex after:items-center after:justify-center after:text-xs after:font-bold">
          Notifikace
        </button>
      </div>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Citace s úvozovkami</h3>
      <blockquote class="before:content-['"'] after:content-['"'] before:text-4xl after:text-4xl before:text-gray-400 after:text-gray-400 p-4 bg-gray-50 rounded italic">
        <span class="text-lg">Toto je citace s automatickými úvozovkami</span>
      </blockquote>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Tooltip (hover efekt)</h3>
      <span class="relative inline-block cursor-help underline decoration-dotted hover:after:content-['Tohle_je_tooltip'] hover:after:absolute hover:after:left-1/2 hover:after:-translate-x-1/2 hover:after:top-full hover:after:mt-2 hover:after:bg-gray-800 hover:after:text-white hover:after:px-3 hover:after:py-1 hover:after:rounded hover:after:text-sm hover:after:whitespace-nowrap">
        Najeď myší
      </span>
    </div>
  </section>

  <!-- ============================================ -->
  <!-- #7 SUPPORTS QUERIES -->
  <!-- ============================================ -->
  <section class="mb-12 bg-white p-6 rounded-lg border-2 border-pink-200">
    <h2 class="text-2xl font-bold mb-4 text-pink-700">#7 Supports queries (detekce CSS features)</h2>
    <p class="text-gray-600 mb-4">Aplikuj styly jen když prohlížeč podporuje feature</p>
    <div class="bg-gray-50 p-4 rounded mb-4">
      <code class="text-sm">supports-[backdrop-filter]:backdrop-blur-lg</code>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Backdrop blur (fallback)</h3>
      <div class="relative h-40 bg-gradient-to-r from-blue-400 to-purple-500 rounded overflow-hidden">
        <img src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400" class="w-full h-full object-cover" alt="pozadí" />
        <div class="absolute inset-0 flex items-center justify-center">
          <div class="bg-white/30 supports-[backdrop-filter]:backdrop-blur-md supports-[backdrop-filter]:bg-white/20 p-6 rounded-lg">
            <p class="font-bold text-white">
              S podporou: blur efekt<br/>
              Bez podpory: silnější opacity
            </p>
          </div>
        </div>
      </div>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Grid layout (fallback na flex)</h3>
      <div class="flex flex-wrap gap-4 supports-[display:grid]:grid supports-[display:grid]:grid-cols-3">
        <div class="p-4 bg-blue-100 rounded">Box 1</div>
        <div class="p-4 bg-blue-100 rounded">Box 2</div>
        <div class="p-4 bg-blue-100 rounded">Box 3</div>
      </div>
      <p class="text-sm text-gray-600 mt-2">S podporou Grid: 3 sloupce | Bez: flex wrap</p>
    </div>
  </section>

  <!-- ============================================ -->
  <!-- #10 ARBITRARY PROPERTIES -->
  <!-- ============================================ -->
  <section class="mb-12 bg-white p-6 rounded-lg border-2 border-indigo-200">
    <h2 class="text-2xl font-bold mb-4 text-indigo-700">#10 Arbitrary properties (vlastní CSS)</h2>
    <p class="text-gray-600 mb-4">Použij libovolnou CSS property</p>
    <div class="bg-gray-50 p-4 rounded mb-4">
      <code class="text-sm">[property:value]</code>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Mask image (gradient fade)</h3>
      <div class="[mask-image:linear-gradient(to_bottom,black_50%,transparent)] bg-gradient-to-r from-purple-400 to-pink-400 h-32 flex items-center justify-center text-white font-bold">
        Fade efekt dole
      </div>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Text stroke</h3>
      <h2 class="text-5xl font-bold text-transparent [-webkit-text-stroke:2px_black]">
        OUTLINED TEXT
      </h2>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Custom scrollbar</h3>
      <div class="h-32 overflow-y-scroll border-2 border-gray-300 rounded p-4 [&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar-track]:bg-gray-100 [&::-webkit-scrollbar-thumb]:bg-blue-500 [&::-webkit-scrollbar-thumb]:rounded">
        <p>Scrolluj dolů a uvidíš vlastní scrollbar.</p>
        <p class="mt-4">Lorem ipsum dolor sit amet.</p>
        <p class="mt-4">Lorem ipsum dolor sit amet.</p>
        <p class="mt-4">Lorem ipsum dolor sit amet.</p>
        <p class="mt-4">Lorem ipsum dolor sit amet.</p>
        <p class="mt-4">Konec obsahu.</p>
      </div>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Aspect ratio nestandardní</h3>
      <div class="[aspect-ratio:21/9] bg-gradient-to-r from-orange-400 to-red-400 rounded flex items-center justify-center text-white font-bold">
        21:9 Ultrawide poměr
      </div>
    </div>
  </section>

  <!-- ============================================ -->
  <!-- #13 ARBITRARY VARIANTS S MODIFIERS -->
  <!-- ============================================ -->
  <section class="mb-12 bg-white p-6 rounded-lg border-2 border-teal-200">
    <h2 class="text-2xl font-bold mb-4 text-teal-700">#13 Arbitrary variants s media queries</h2>
    <p class="text-gray-600 mb-4">Vlastní podmínky a media queries</p>
    <div class="bg-gray-50 p-4 rounded mb-4">
      <code class="text-sm">[@media(hover:hover)]:hover:scale-110</code>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Hover jen na zařízeních s hover (ne mobil)</h3>
      <button class="px-6 py-3 bg-blue-500 text-white rounded transition [@media(hover:hover)]:hover:scale-110 [@media(hover:hover)]:hover:bg-blue-600">
        Hover efekt jen na PC/notebooku
      </button>
      <p class="text-sm text-gray-600 mt-2">Na mobilu (touch) se škálování neaplikuje</p>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Dark mode s prefers-color-scheme</h3>
      <div class="p-4 border-2 rounded bg-white [@media(prefers-color-scheme:dark)]:bg-gray-800 [@media(prefers-color-scheme:dark)]:text-white [@media(prefers-color-scheme:dark)]:border-gray-600">
        Tento box reaguje na systémové nastavení dark mode
      </div>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Orientace zařízení</h3>
      <div class="p-4 border-2 rounded bg-blue-100 [@media(orientation:portrait)]:bg-green-100 [@media(orientation:landscape)]:bg-orange-100">
        <p class="font-semibold">Otoč zařízení:</p>
        <p class="text-sm">Portrait (na výšku) = zelená</p>
        <p class="text-sm">Landscape (na šířku) = oranžová</p>
      </div>
    </div>

    <div class="mb-6">
      <h3 class="font-semibold mb-3">Vlastní breakpoint</h3>
      <div class="p-4 border-2 rounded bg-gray-100 [@media(min-width:800px)]:bg-purple-100 [@media(min-width:800px)]:text-purple-800">
        Pod 800px: šedá | Nad 800px: fialová
      </div>
    </div>
  </section>

  <!-- ============================================ -->
  <!-- SHRNUTÍ -->
  <!-- ============================================ -->
  <section class="bg-gradient-to-r from-blue-50 to-purple-50 p-6 rounded-lg border-2 border-blue-300">
    <h2 class="text-2xl font-bold mb-6">📚 Rychlé shrnutí</h2>
    
    <div class="grid md:grid-cols-2 gap-4">
      <div class="bg-white p-4 rounded">
        <h3 class="font-bold text-blue-600 mb-2">#2 Peer</h3>
        <code class="text-xs">peer-checked:text-blue-500</code>
        <p class="text-sm text-gray-600 mt-1">Sourozenec ovlivňuje sourozence</p>
      </div>

      <div class="bg-white p-4 rounded">
        <h3 class="font-bold text-green-600 mb-2">#4 Multiple conditions</h3>
        <code class="text-xs">data-[a]:data-[b]:class</code>
        <p class="text-sm text-gray-600 mt-1">Obě podmínky musí platit (AND)</p>
      </div>

      <div class="bg-white p-4 rounded">
        <h3 class="font-bold text-purple-600 mb-2">#5 Nth-child</h3>
        <code class="text-xs">[&>*:nth-child(odd)]:bg-gray</code>
        <p class="text-sm text-gray-600 mt-1">Stylování podle pozice</p>
      </div>

      <div class="bg-white p-4 rounded">
        <h3 class="font-bold text-orange-600 mb-2">#6 Before/After</h3>
        <code class="text-xs">before:content-['→']</code>
        <p class="text-sm text-gray-600 mt-1">Pseudo-elementy s obsahem</p>
      </div>

      <div class="bg-white p-4 rounded">
        <h3 class="font-bold text-pink-600 mb-2">#7 Supports</h3>
        <code class="text-xs">supports-[backdrop-filter]:blur</code>
        <p class="text-sm text-gray-600 mt-1">Feature detection + fallback</p>
      </div>

      <div class="bg-white p-4 rounded">
        <h3 class="font-bold text-indigo-600 mb-2">#10 Arbitrary properties</h3>
        <code class="text-xs">[mask-image:gradient]</code>
        <p class="text-sm text-gray-600 mt-1">Libovolné CSS properties</p>
      </div>

      <div class="bg-white p-4 rounded">
        <h3 class="font-bold text-teal-600 mb-2">#13 Arbitrary variants</h3>
        <code class="text-xs">[@media(hover:hover)]:hover</code>
        <p class="text-sm text-gray-600 mt-1">Vlastní media queries</p>
      </div>
    </div>
  </section>

</body>
</html>

On gists

Tailwind super selectors (&, data-*, group ...)

Popular ⭐ Tailwind CSS

index.html #

<!doctype html>
<html lang="cs">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Tailwind Selektory - Kompletní průvodce</title>
    <script src="https://cdn.tailwindcss.com"></script>
  </head>
  <body class="bg-gray-50 p-8">
    <h1 class="mb-8 text-3xl font-bold">Tailwind CSS - Kompletní průvodce selektory</h1>

    <!-- ============================================ -->
    <!-- ČÁST 1: STYLOVÁNÍ SAMOTNÉHO ELEMENTU -->
    <!-- ============================================ -->
    <section class="mb-16 rounded-lg border-2 border-blue-200 bg-white p-6">
      <h2 class="mb-6 text-2xl font-bold text-blue-700">ČÁST 1: Stylování samotného elementu</h2>

      <!-- #1 Data atribut bez hodnoty -->
      <div class="mb-8">
        <h3 class="mb-3 text-lg font-semibold">#1 Data atribut BEZ hodnoty na elementu</h3>
        <div class="mb-2 rounded bg-gray-50 p-4"><code class="text-sm">data-[active]:class</code> nebo <code class="text-sm">data-active:class</code></div>
        <div data-active class="border-2 border-gray-300 p-4 data-[active]:border-purple-500 data-[active]:bg-purple-50">✅ Mám data-active → fialový</div>
        <div class="mt-2 border-2 border-gray-300 p-4 data-[active]:border-purple-500 data-[active]:bg-purple-50">❌ Nemám data-active → šedý</div>
      </div>

      <!-- #2 Data atribut S hodnotou -->
      <div class="mb-8">
        <h3 class="mb-3 text-lg font-semibold">#2 Data atribut S konkrétní hodnotou na elementu</h3>
        <div class="mb-2 rounded bg-gray-50 p-4"><code class="text-sm">data-[state=open]:class</code> - kontroluje přesnou hodnotu</div>
        <div data-state="open" class="border-2 border-gray-300 p-4 data-[state=open]:border-green-500 data-[state=open]:bg-green-50">✅ data-state="open" → zelený</div>
        <div data-state="closed" class="mt-2 border-2 border-gray-300 p-4 data-[state=open]:border-green-500 data-[state=open]:bg-green-50">❌ data-state="closed" → šedý</div>
      </div>

      <!-- #3 Data atribut - jen přítomnost -->
      <div class="mb-8">
        <h3 class="mb-3 text-lg font-semibold">#3 Data atribut - kontrola jen PŘÍTOMNOSTI (ignoruje hodnotu)</h3>
        <div class="mb-2 rounded bg-gray-50 p-4"><code class="text-sm">data-state:class</code> - stačí, že atribut existuje</div>
        <div data-state="open" class="border-2 border-gray-300 p-4 data-state:border-orange-500 data-state:bg-orange-50">✅ data-state="open" → oranžový</div>
        <div data-state="closed" class="mt-2 border-2 border-gray-300 p-4 data-state:border-orange-500 data-state:bg-orange-50">✅ data-state="closed" → také oranžový (hodnota se ignoruje)</div>
        <div class="mt-2 border-2 border-gray-300 p-4 data-state:border-orange-500 data-state:bg-orange-50">❌ bez data-state → šedý</div>
      </div>

      <!-- #4 CSS třída -->
      <div class="mb-8">
        <h3 class="mb-3 text-lg font-semibold">#4 CSS třída na elementu</h3>
        <div class="mb-2 rounded bg-gray-50 p-4">
          <code class="text-sm">[&.active]:class</code>
        </div>
        <div class="active border-2 border-gray-300 p-4 [&.active]:border-blue-500 [&.active]:bg-blue-50">✅ Mám třídu .active → modrý</div>
        <div class="mt-2 border-2 border-gray-300 p-4 [&.active]:border-blue-500 [&.active]:bg-blue-50">❌ Nemám třídu .active → šedý</div>
      </div>
    </section>

    <!-- ============================================ -->
    <!-- ČÁST 2: STYLOVÁNÍ PODLE RODIČE (GROUP) -->
    <!-- ============================================ -->
    <section class="mb-16 rounded-lg border-2 border-green-200 bg-white p-6">
      <h2 class="mb-6 text-2xl font-bold text-green-700">ČÁST 2: Stylování podle rodiče s GROUP (čistší syntaxe)</h2>
      <p class="mb-6 text-gray-600">Rodič musí mít třídu <code class="rounded bg-gray-100 px-2 py-1">group</code></p>

      <!-- #5 Group + data atribut bez hodnoty -->
      <div class="mb-8">
        <h3 class="mb-3 text-lg font-semibold">#5 Group + data atribut BEZ hodnoty</h3>
        <div class="mb-2 rounded bg-gray-50 p-4">
          <code class="text-sm">group-data-[active]:class</code>
        </div>
        <div data-active class="group border-2 border-gray-300 bg-gray-100 p-4">
          <p class="group-data-[active]:font-bold group-data-[active]:text-purple-600">✅ Rodič (group) má data-active → fialový text</p>
        </div>
        <div class="group mt-2 border-2 border-gray-300 bg-gray-100 p-4">
          <p class="group-data-[active]:font-bold group-data-[active]:text-purple-600">❌ Rodič nemá data-active → normální</p>
        </div>
      </div>

      <!-- #6 Group + data atribut s hodnotou -->
      <div class="mb-8">
        <h3 class="mb-3 text-lg font-semibold">#6 Group + data atribut S hodnotou</h3>
        <div class="mb-2 rounded bg-gray-50 p-4">
          <code class="text-sm">group-data-[state=danger]:class</code>
        </div>
        <div data-state="danger" class="group border-2 border-gray-300 bg-gray-100 p-4">
          <p class="group-data-[state=danger]:font-bold group-data-[state=danger]:text-red-600">✅ Rodič má data-state="danger" → červený</p>
        </div>
        <div data-state="success" class="group mt-2 border-2 border-gray-300 bg-gray-100 p-4">
          <p class="group-data-[state=danger]:font-bold group-data-[state=danger]:text-red-600">❌ Rodič má data-state="success" → normální</p>
        </div>
      </div>

      <!-- #7 Group + CSS třída -->
      <div class="mb-8">
        <h3 class="mb-3 text-lg font-semibold">#7 Group + CSS třída</h3>
        <div class="mb-2 rounded bg-gray-50 p-4">
          <code class="text-sm">group-[.card--danger]:class</code>
        </div>
        <div class="group card--danger border-2 border-gray-300 bg-orange-600 p-4">
          <a href="#" class="text-black group-[.card--danger]:font-bold group-[.card--danger]:text-white"> ✅ Rodič má .card--danger → bílý text </a>
        </div>
        <div class="group mt-2 border-2 border-gray-300 bg-gray-100 p-4">
          <a href="#" class="text-black group-[.card--danger]:font-bold group-[.card--danger]:text-white"> ❌ Rodič nemá .card--danger → černý text </a>
        </div>
      </div>
    </section>

    <!-- ============================================ -->
    <!-- ČÁST 3: STYLOVÁNÍ PODLE PŘEDKA (ARBITRARY) -->
    <!-- ============================================ -->
    <section class="mb-16 rounded-lg border-2 border-purple-200 bg-white p-6">
      <h2 class="mb-6 text-2xl font-bold text-purple-700">ČÁST 3: Stylování podle předka bez GROUP (flexibilnější)</h2>
      <p class="mb-6 text-gray-600">Rodič/předek <strong>nemusí</strong> mít třídu <code class="rounded bg-gray-100 px-2 py-1">group</code></p>

      <!-- #8 Arbitrary + data atribut bez hodnoty -->
      <div class="mb-8">
        <h3 class="mb-3 text-lg font-semibold">#8 Arbitrary selektor + data atribut BEZ hodnoty</h3>
        <div class="mb-2 rounded bg-gray-50 p-4">
          <code class="text-sm">[[data-expanded]_&]:class</code>
        </div>
        <div data-expanded class="border-2 border-gray-300 bg-gray-100 p-4">
          <p class="[[data-expanded]_&]:font-bold [[data-expanded]_&]:text-purple-600">✅ Předek má data-expanded → fialový</p>
        </div>
        <div class="mt-2 border-2 border-gray-300 bg-gray-100 p-4">
          <p class="[[data-expanded]_&]:font-bold [[data-expanded]_&]:text-purple-600">❌ Předek nemá data-expanded → normální</p>
        </div>
      </div>

      <!-- #9 Arbitrary + data atribut s hodnotou -->
      <div class="mb-8">
        <h3 class="mb-3 text-lg font-semibold">#9 Arbitrary selektor + data atribut S hodnotou</h3>
        <div class="mb-2 rounded bg-gray-50 p-4">
          <code class="text-sm">[[data-theme='dark']_&]:class</code>
        </div>
        <div data-theme="dark" class="border-2 border-gray-300 bg-gray-800 p-4">
          <p class="text-gray-400 [[data-theme='dark']_&]:font-bold [[data-theme='dark']_&]:text-orange-400">✅ Předek má data-theme="dark" → oranžový</p>
        </div>
        <div data-theme="light" class="mt-2 border-2 border-gray-300 bg-gray-100 p-4">
          <p class="text-gray-400 [[data-theme='dark']_&]:font-bold [[data-theme='dark']_&]:text-orange-400">❌ Předek má data-theme="light" → šedý</p>
        </div>
      </div>

      <!-- #10 Arbitrary + CSS třída -->
      <div class="mb-8">
        <h3 class="mb-3 text-lg font-semibold">#10 Arbitrary selektor + CSS třída</h3>
        <div class="mb-2 rounded bg-gray-50 p-4">
          <code class="text-sm">[.is-open_&]:class</code>
        </div>
        <div class="is-open border-2 border-gray-300 bg-gray-100 p-4">
          <p class="[.is-open_&]:font-bold [.is-open_&]:text-blue-600">✅ Předek má .is-open → modrý</p>
        </div>
        <div class="mt-2 border-2 border-gray-300 bg-gray-100 p-4">
          <p class="[.is-open_&]:font-bold [.is-open_&]:text-blue-600">❌ Předek nemá .is-open → normální</p>
        </div>
      </div>
    </section>

    <!-- ============================================ -->
    <!-- ČÁST 4: STYLOVÁNÍ POTOMKA PODLE RODIČE -->
    <!-- ============================================ -->
    <section class="mb-16 rounded-lg border-2 border-orange-200 bg-white p-6">
      <h2 class="mb-6 text-2xl font-bold text-orange-700">ČÁST 4: Stylování POTOMKA podle stavu RODIČE</h2>
      <p class="mb-6 text-gray-600">Rodič má atribut/třídu a stylujeme jeho potomka (dítě, vnuka...)</p>

      <!-- #11 Potomek podle data atributu rodiče -->
      <div class="mb-8">
        <h3 class="mb-3 text-lg font-semibold">#11 Stylování SVG podle data atributu rodiče</h3>
        <div class="mb-2 rounded bg-gray-50 p-4">
          <code class="text-sm">[&[data-collapsed=true]_svg]:rotate-180</code>
          <p class="mt-1 text-xs text-gray-600">Čteš to: "Když <strong>tento element</strong> má data-collapsed=true, otoč jeho <strong>svg potomka</strong>"</p>
        </div>
        <div data-collapsed="true" class="border-2 border-gray-300 bg-gray-100 p-4 [&[data-collapsed=true]_svg]:rotate-180 [&[data-collapsed=true]_svg]:text-red-500">
          <svg class="h-6 w-6 transition-transform" fill="currentColor" viewBox="0 0 20 20">
            <path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" />
          </svg>
          <span class="ml-2">✅ Rodič má data-collapsed="true" → SVG je otočené a červené</span>
        </div>
        <div class="mt-2 border-2 border-gray-300 bg-gray-100 p-4 [&[data-collapsed=true]_svg]:rotate-180 [&[data-collapsed=true]_svg]:text-red-500">
          <svg class="h-6 w-6 transition-transform" fill="currentColor" viewBox="0 0 20 20">
            <path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" />
          </svg>
          <span class="ml-2">❌ Rodič nemá data-collapsed → SVG normální</span>
        </div>
      </div>

      <!-- #12 Potomek podle CSS třídy rodiče -->
      <div class="mb-8">
        <h3 class="mb-3 text-lg font-semibold">#12 Stylování potomka podle CSS třídy rodiče</h3>
        <div class="mb-2 rounded bg-gray-50 p-4">
          <code class="text-sm">[&.card--premium_span]:text-yellow-500</code>
          <p class="mt-1 text-xs text-gray-600">Čteš to: "Když <strong>tento element</strong> má třídu .card--premium, nastyl jeho <strong>span potomka</strong>"</p>
        </div>
        <div class="card--premium border-2 border-gray-300 bg-gradient-to-r from-yellow-50 to-orange-50 p-4 [&.card--premium_span]:font-bold [&.card--premium_span]:text-yellow-600">
          <span>⭐</span>
          <span class="ml-2">✅ Rodič má .card--premium → span je žlutý a tučný</span>
        </div>
        <div class="mt-2 border-2 border-gray-300 bg-gray-100 p-4 [&.card--premium_span]:font-bold [&.card--premium_span]:text-yellow-600">
          <span>⭐</span>
          <span class="ml-2">❌ Rodič nemá .card--premium → span normální</span>
        </div>
      </div>

      <!-- #13 Složitější příklad - více úrovní -->
      <div class="mb-8">
        <h3 class="mb-3 text-lg font-semibold">#13 Komplexnější příklad - vnořené elementy</h3>
        <div class="mb-2 rounded bg-gray-50 p-4">
          <code class="text-sm">[&[data-state=error]_.icon]:text-red-500</code>
          <p class="mt-1 text-xs text-gray-600">Styluje <strong>jakéhokoliv potomka</strong> s třídou .icon</p>
        </div>
        <div data-state="error" class="border-2 border-red-300 bg-red-50 p-4 [&[data-state=error]_.icon]:scale-125 [&[data-state=error]_.icon]:text-red-600">
          <div class="flex items-center gap-2">
            <span class="icon transition-transform">⚠️</span>
            <span>✅ Rodič má data-state="error" → ikona je větší a červená</span>
          </div>
          <div class="mt-2 ml-8">
            <span class="icon transition-transform">❌</span>
            <span class="ml-2">Vnořená ikona je také stylovaná</span>
          </div>
        </div>
        <div class="mt-2 border-2 border-gray-300 bg-gray-100 p-4 [&[data-state=error]_.icon]:scale-125 [&[data-state=error]_.icon]:text-red-600">
          <div class="flex items-center gap-2">
            <span class="icon transition-transform">⚠️</span>
            <span>❌ Rodič nemá data-state="error" → ikona normální</span>
          </div>
        </div>
      </div>

      <div class="rounded border-l-4 border-orange-400 bg-orange-50 p-4">
        <p class="mb-2 font-semibold text-orange-800">🔑 Klíčový rozdíl:</p>
        <div class="space-y-2 text-sm">
          <div>
            <code class="rounded bg-white px-2 py-1">[[data-collapsed=true]_&]:class</code>
            <p class="ml-4 text-gray-700">→ Styluje <strong>tento element</strong>, když <strong>předek</strong> má atribut</p>
          </div>
          <div>
            <code class="rounded bg-white px-2 py-1">[&[data-collapsed=true]_svg]:class</code>
            <p class="ml-4 text-gray-700">→ Styluje <strong>potomka</strong>, když <strong>tento element</strong> má atribut</p>
          </div>
        </div>
      </div>
    </section>

    <!-- ============================================ -->
    <!-- KDY CO POUŽÍT -->
    <!-- ============================================ -->
    <section class="mb-8 rounded-lg border-2 border-blue-300 bg-gradient-to-r from-blue-50 to-purple-50 p-6">
      <h2 class="mb-6 text-2xl font-bold">🎯 Kdy co použít?</h2>

      <div class="space-y-4">
        <div class="rounded-lg bg-white p-4">
          <h3 class="mb-2 font-bold text-green-700">✅ Použij GROUP syntaxe když:</h3>
          <ul class="ml-6 list-disc space-y-1 text-gray-700">
            <li>Máš jasný rodič-dítě vztah v komponentě</li>
            <li>Chceš čitelnější a kratší kód</li>
            <li>Používáš to opakovaně v jedné komponentě</li>
          </ul>
          <div class="mt-3 rounded bg-green-50 p-3 text-sm">
            <code>group-data-[active]:text-purple-500</code>
          </div>
        </div>

        <div class="rounded-lg bg-white p-4">
          <h3 class="mb-2 font-bold text-purple-700">✅ Použij ARBITRARY selektory když:</h3>
          <ul class="ml-6 list-disc space-y-1 text-gray-700">
            <li>Nechceš přidávat třídu <code class="rounded bg-gray-100 px-1">group</code> na rodiče</li>
            <li>Předek je vzdálenější (ne přímý rodič)</li>
            <li>Potřebuješ flexibilnější podmínky</li>
          </ul>
          <div class="mt-3 rounded bg-purple-50 p-3 text-sm">
            <code>[[data-theme='dark']_&]:text-white</code>
          </div>
        </div>

        <div class="rounded-lg bg-white p-4">
          <h3 class="mb-2 font-bold text-orange-700">✅ Stylování potomka podle rodiče:</h3>
          <ul class="ml-6 list-disc space-y-1 text-gray-700">
            <li>Když chceš stylovat dítě/vnuka podle stavu rodiče</li>
            <li>Rodič má atribut/třídu a potomek se podle toho mění</li>
            <li>Užitečné pro ikony, SVG, nested elementy</li>
          </ul>
          <div class="mt-3 rounded bg-orange-50 p-3 text-sm">
            <code>[&[data-collapsed=true]_svg]:rotate-180</code>
          </div>
        </div>

        <div class="rounded-lg bg-white p-4">
          <h3 class="mb-2 font-bold text-blue-700">✅ Přímé selektory na elementu:</h3>
          <ul class="ml-6 list-disc space-y-1 text-gray-700">
            <li>Když stylování závisí jen na vlastním stavu elementu</li>
            <li>Nejjednodušší a nejrychlejší varianta</li>
          </ul>
          <div class="mt-3 rounded bg-blue-50 p-3 text-sm">
            <code>data-[active]:bg-blue-500</code>
          </div>
        </div>
      </div>
    </section>

    <!-- ============================================ -->
    <!-- RYCHLÁ REFERENČNÍ TABULKA -->
    <!-- ============================================ -->
    <section class="rounded-lg border-2 border-gray-300 bg-white p-6">
      <h2 class="mb-6 text-2xl font-bold">📋 Rychlá referenční tabulka</h2>

      <div class="overflow-x-auto">
        <table class="w-full border-collapse text-sm">
          <thead>
            <tr class="bg-gray-100">
              <th class="border p-3 text-left">Situace</th>
              <th class="border p-3 text-left">Syntaxe</th>
              <th class="border p-3 text-left">Příklad</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td class="border p-3 font-semibold" colspan="3">NA ELEMENTU:</td>
            </tr>
            <tr>
              <td class="border p-3">Data atribut bez hodnoty</td>
              <td class="border p-3"><code>data-[active]:class</code></td>
              <td class="border p-3"><code>data-[active]:bg-blue-500</code></td>
            </tr>
            <tr>
              <td class="border p-3">Data atribut s hodnotou</td>
              <td class="border p-3"><code>data-[state=open]:class</code></td>
              <td class="border p-3"><code>data-[state=open]:text-green-500</code></td>
            </tr>
            <tr>
              <td class="border p-3">Data atribut - jen přítomnost</td>
              <td class="border p-3"><code>data-state:class</code></td>
              <td class="border p-3"><code>data-state:border-orange-500</code></td>
            </tr>
            <tr>
              <td class="border p-3">CSS třída</td>
              <td class="border p-3"><code>[&.active]:class</code></td>
              <td class="border p-3"><code>[&.active]:font-bold</code></td>
            </tr>

            <tr>
              <td class="border p-3 font-semibold" colspan="3">NA RODIČI (s GROUP):</td>
            </tr>
            <tr>
              <td class="border p-3">Data atribut bez hodnoty</td>
              <td class="border p-3"><code>group-data-[active]:class</code></td>
              <td class="border p-3"><code>group-data-[active]:text-purple-500</code></td>
            </tr>
            <tr>
              <td class="border p-3">Data atribut s hodnotou</td>
              <td class="border p-3"><code>group-data-[state=open]:class</code></td>
              <td class="border p-3"><code>group-data-[state=open]:block</code></td>
            </tr>
            <tr>
              <td class="border p-3">CSS třída</td>
              <td class="border p-3"><code>group-[.danger]:class</code></td>
              <td class="border p-3"><code>group-[.danger]:text-red-500</code></td>
            </tr>

            <tr>
              <td class="border p-3 font-semibold" colspan="3">NA PŘEDKOVI (bez GROUP):</td>
            </tr>
            <tr>
              <td class="border p-3">Data atribut bez hodnoty</td>
              <td class="border p-3"><code>[[data-expanded]_&]:class</code></td>
              <td class="border p-3"><code>[[data-expanded]_&]:block</code></td>
            </tr>
            <tr>
              <td class="border p-3">Data atribut s hodnotou</td>
              <td class="border p-3"><code>[[data-theme='dark']_&]:class</code></td>
              <td class="border p-3"><code>[[data-theme='dark']_&]:text-white</code></td>
            </tr>
            <tr>
              <td class="border p-3">CSS třída</td>
              <td class="border p-3"><code>[.parent_&]:class</code></td>
              <td class="border p-3"><code>[.is-open_&]:opacity-100</code></td>
            </tr>

            <tr>
              <td class="border p-3 font-semibold" colspan="3">POTOMEK PODLE RODIČE:</td>
            </tr>
            <tr>
              <td class="border p-3">Potomek podle data atributu</td>
              <td class="border p-3"><code>[&[data-collapsed=true]_svg]:class</code></td>
              <td class="border p-3"><code>[&[data-collapsed=true]_svg]:rotate-180</code></td>
            </tr>
            <tr>
              <td class="border p-3">Potomek podle CSS třídy</td>
              <td class="border p-3"><code>[&.premium_span]:class</code></td>
              <td class="border p-3"><code>[&.premium_span]:text-yellow-500</code></td>
            </tr>
            <tr>
              <td class="border p-3">Potomek s třídou</td>
              <td class="border p-3"><code>[&[data-state=error]_.icon]:class</code></td>
              <td class="border p-3"><code>[&[data-state=error]_.icon]:text-red-500</code></td>
            </tr>
          </tbody>
        </table>
      </div>
    </section>
  </body>
</html>

On gists

Reactivity in composables

Popular ⭐ Vue.js

App.vue #

<template>
  <TheBase :state="state" />
</template>


<script setup>
import { ref } from 'vue'
import TheBase from './TheBase.vue'


const state = ref(111)

setTimeout(() => {
  state.value = 222
}, 2000)
</script>

On gists

Compute & transform logic outside of UI component

Popular ⭐ Vue.js

index.md #

Vue.js Media Transformer - Všechny varianty implementace

1. Composable (Vue 3 Composition API)

// composables/useMediaObjects.js
export function useMediaObjects() {
  const transformMediaObjects = (productGallery, productVideos) => {
    const finalObjects = []

    // Photos
    if (productGallery) {
      finalObjects.push(
        ...productGallery.map((photo, index) => ({
          ...photo,
          type: 'photo',
          rank: index + 1
        }))
      )
    }

    // Videos
    if (productVideos) {
      finalObjects.push(
        ...productVideos.map((videoUrl, index) => {
          const youtubeId = extractYouTubeId(videoUrl)
          return {
            url: videoUrl,
            type: 'video',
            rank: finalObjects.length + index + 1,
            previewThumb: youtubeId 
              ? `https://img.youtube.com/vi/${youtubeId}/sddefault.jpg` 
              : null
          }
        })
      )
    }

    return finalObjects
  }

  const extractYouTubeId = (url) => {
    return url.match(
      /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/
    )?.[1]
  }

  return {
    transformMediaObjects,
    extractYouTubeId
  }
}

Použití v komponentě:

// Product.vue
<script setup>
import { computed } from 'vue'
import { useMediaObjects } from '@/composables/useMediaObjects'

const props = defineProps(['productGallery', 'product'])

const { transformMediaObjects } = useMediaObjects()

const mediaObjects = computed(() => {
  return transformMediaObjects(props.productGallery, props.product?.product_video)
})
</script>

2. JS Třída - Statické metody

// services/MediaTransformer.js
export class MediaTransformer {
  static transform(productGallery, productVideos) {
    const finalObjects = []

    if (productGallery) {
      finalObjects.push(
        ...productGallery.map((photo, index) => ({
          ...photo,
          type: 'photo',
          rank: index + 1
        }))
      )
    }

    if (productVideos) {
      finalObjects.push(
        ...productVideos.map((videoUrl, index) => {
          const youtubeId = this.extractYouTubeId(videoUrl)
          return {
            url: videoUrl,
            type: 'video',
            rank: finalObjects.length + index + 1,
            previewThumb: youtubeId 
              ? `https://img.youtube.com/vi/${youtubeId}/sddefault.jpg` 
              : null
          }
        })
      )
    }

    return finalObjects
  }

  static extractYouTubeId(url) {
    return url.match(
      /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/
    )?.[1]
  }

  static generateThumbnail(youtubeId, quality = 'sddefault') {
    return `https://img.youtube.com/vi/${youtubeId}/${quality}.jpg`
  }
}

Použití:

// Product.vue
<script setup>
import { computed } from 'vue'
import { MediaTransformer } from '@/services/MediaTransformer'

const props = defineProps(['productGallery', 'product'])

const mediaObjects = computed(() => {
  return MediaTransformer.transform(props.productGallery, props.product?.product_video)
})
</script>

3. JS Třída - Export třídy (instance v komponentě)

// services/MediaTransformer.js
export class MediaTransformer {
  constructor(config = {}) {
    this.thumbnailQuality = config.thumbnailQuality || 'sddefault'
    this.defaultVideoType = config.defaultVideoType || 'video'
  }

  transform(productGallery, productVideos) {
    const finalObjects = []

    if (productGallery) {
      finalObjects.push(
        ...productGallery.map((photo, index) => ({
          ...photo,
          type: 'photo',
          rank: index + 1
        }))
      )
    }

    if (productVideos) {
      finalObjects.push(
        ...productVideos.map((videoUrl, index) => {
          const youtubeId = this.extractYouTubeId(videoUrl)
          return {
            url: videoUrl,
            type: this.defaultVideoType,
            rank: finalObjects.length + index + 1,
            previewThumb: youtubeId 
              ? `https://img.youtube.com/vi/${youtubeId}/${this.thumbnailQuality}.jpg` 
              : null
          }
        })
      )
    }

    return finalObjects
  }

  extractYouTubeId(url) {
    return url.match(
      /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/
    )?.[1]
  }

  setConfig(newConfig) {
    Object.assign(this, newConfig)
    return this
  }
}

Použití:

// Product.vue
<script setup>
import { computed, ref } from 'vue'
import { MediaTransformer } from '@/services/MediaTransformer'

const props = defineProps(['productGallery', 'product'])

const mediaTransformer = ref(new MediaTransformer({ thumbnailQuality: 'hqdefault' }))

const mediaObjects = computed(() => {
  return mediaTransformer.value.transform(props.productGallery, props.product?.product_video)
})
</script>

4. JS Třída - Export instance (singleton)

// services/MediaTransformer.js
class MediaTransformer {
  constructor(config = {}) {
    this.thumbnailQuality = config.thumbnailQuality || 'sddefault'
  }

  transform(productGallery, productVideos) {
    // stejná implementace jako výše...
  }

  extractYouTubeId(url) {
    // stejná implementace...
  }

  setConfig(newConfig) {
    Object.assign(this, newConfig)
    return this
  }
}

// Export instance
export const mediaTransformer = new MediaTransformer()

// Případně export obou
export { MediaTransformer }

Použití:

// Product.vue
<script setup>
import { computed } from 'vue'
import { mediaTransformer } from '@/services/MediaTransformer'

const props = defineProps(['productGallery', 'product'])

const mediaObjects = computed(() => {
  return mediaTransformer.transform(props.productGallery, props.product?.product_video)
})

// Změna konfigurace kdykoliv:
// mediaTransformer.setConfig({ thumbnailQuality: 'maxresdefault' })
</script>

5. Singleton Pattern

// services/MediaTransformer.js
class MediaTransformer {
  static _instance = null

  constructor(config = {}) {
    if (MediaTransformer._instance) {
      return MediaTransformer._instance
    }

    this.thumbnailQuality = config.thumbnailQuality || 'sddefault'
    MediaTransformer._instance = this
  }

  static getInstance(config = {}) {
    if (!this._instance) {
      this._instance = new MediaTransformer(config)
    }
    return this._instance
  }

  transform(productGallery, productVideos) {
    // implementace...
  }

  extractYouTubeId(url) {
    // implementace...
  }
}

export default MediaTransformer

Použití:

// Product.vue
<script setup>
import { computed } from 'vue'
import MediaTransformer from '@/services/MediaTransformer'

const props = defineProps(['productGallery', 'product'])

const mediaObjects = computed(() => {
  const transformer = MediaTransformer.getInstance({ thumbnailQuality: 'hqdefault' })
  return transformer.transform(props.productGallery, props.product?.product_video)
})
</script>

5.1 Singleton with Registry

class MediaTransformer {
  static _instances = new Map()

  constructor(config = {}) {
    this.thumbnailQuality = config.thumbnailQuality || 'sddefault'
    this.config = config
  }

  static getInstance(config = {}) {
    // Vytvoř unikátní klíč z configu
    const key = JSON.stringify(config)

    if (!this._instances.has(key)) {
      this._instances.set(key, new MediaTransformer(config))
    }

    return this._instances.get(key)
  }

  transform(productGallery, productVideos) {
    // implementace...
  }
}

const hq = MediaTransformer.getInstance({ thumbnailQuality: 'hqdefault' })
const max = MediaTransformer.getInstance({ thumbnailQuality: 'maxresdefault' })
// Různé instance ✅

5.2 New class with static fn

class MediaTransformer {
  constructor(config = {}) {
    this.thumbnailQuality = config.thumbnailQuality || 'sddefault'
  }

  transform(productGallery, productVideos) {
    // implementace...
  }

  static transform(productGallery, productVideos, config = {}) {
    const instance = new MediaTransformer(config)
    return instance.transform(productGallery, productVideos)
  }
}

export default MediaTransformer

// Varianta 1: Instance
const transformer = new MediaTransformer({ thumbnailQuality: 'hqdefault' })
const media = transformer.transform(gallery, videos)

// Varianta 2: Statická metoda
const media = MediaTransformer.transform(gallery, videos, { thumbnailQuality: 'hqdefault' })

6. Factory Function

// services/MediaTransformer.js
class MediaTransformer {
  constructor(config = {}) {
    this.thumbnailQuality = config.thumbnailQuality || 'sddefault'
  }

  transform(productGallery, productVideos) {
    // implementace...
  }

  extractYouTubeId(url) {
    // implementace...
  }
}

export const createMediaTransformer = (config = {}) => {
  return new MediaTransformer(config)
}

// Export i třídy pro pokročilé použití
export { MediaTransformer }

Použití:

// Product.vue
<script setup>
import { computed, ref } from 'vue'
import { createMediaTransformer } from '@/services/MediaTransformer'

const props = defineProps(['productGallery', 'product'])

const mediaTransformer = ref(createMediaTransformer({ thumbnailQuality: 'maxresdefault' }))

const mediaObjects = computed(() => {
  return mediaTransformer.value.transform(props.productGallery, props.product?.product_video)
})
</script>

7. Plain Object/Service

// services/mediaService.js
export const mediaService = {
  defaultConfig: {
    thumbnailQuality: 'sddefault'
  },

  transform(productGallery, productVideos, config = {}) {
    const finalConfig = { ...this.defaultConfig, ...config }
    const finalObjects = []

    if (productGallery) {
      finalObjects.push(
        ...productGallery.map((photo, index) => ({
          ...photo,
          type: 'photo',
          rank: index + 1
        }))
      )
    }

    if (productVideos) {
      finalObjects.push(
        ...productVideos.map((videoUrl, index) => {
          const youtubeId = this.extractYouTubeId(videoUrl)
          return {
            url: videoUrl,
            type: 'video',
            rank: finalObjects.length + index + 1,
            previewThumb: youtubeId 
              ? `https://img.youtube.com/vi/${youtubeId}/${finalConfig.thumbnailQuality}.jpg` 
              : null
          }
        })
      )
    }

    return finalObjects
  },

  extractYouTubeId(url) {
    return url.match(
      /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/
    )?.[1]
  }
}

Použití:

// Product.vue
<script setup>
import { computed } from 'vue'
import { mediaService } from '@/services/mediaService'

const props = defineProps(['productGallery', 'product'])

const mediaObjects = computed(() => {
  return mediaService.transform(
    props.productGallery, 
    props.product?.product_video,
    { thumbnailQuality: 'hqdefault' }
  )
})
</script>

8. Pure Functions (Helper)

// utils/mediaTransform.js
export const transformMediaObjects = (productGallery, productVideos, config = {}) => {
  const { thumbnailQuality = 'sddefault' } = config
  const finalObjects = []

  if (productGallery) {
    finalObjects.push(
      ...productGallery.map((photo, index) => ({
        ...photo,
        type: 'photo',
        rank: index + 1
      }))
    )
  }

  if (productVideos) {
    finalObjects.push(
      ...productVideos.map((videoUrl, index) => {
        const youtubeId = extractYouTubeId(videoUrl)
        return {
          url: videoUrl,
          type: 'video',
          rank: finalObjects.length + index + 1,
          previewThumb: youtubeId 
            ? `https://img.youtube.com/vi/${youtubeId}/${thumbnailQuality}.jpg` 
            : null
        }
      })
    )
  }

  return finalObjects
}

export const extractYouTubeId = (url) => {
  return url.match(
    /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/
  )?.[1]
}

Použití:

// Product.vue
<script setup>
import { computed } from 'vue'
import { transformMediaObjects } from '@/utils/mediaTransform'

const props = defineProps(['productGallery', 'product'])

const mediaObjects = computed(() => {
  return transformMediaObjects(
    props.productGallery, 
    props.product?.product_video,
    { thumbnailQuality: 'hqdefault' }
  )
})
</script>

Srovnání variant

Varianta Výhody Nevýhody Použití
Composable Vue 3 native, reaktivita, auto-import Vue specific Vue 3 projekty
Statické metody Jednoduché, žádná konfigurace Není flexibilní Jednoduché transformace
Export třídy Flexibilní konfigurace Musíš vytvářet instance Různé konfigurace
Export instance Žádné new, sdílený stav Pevná konfigurace Jednotná konfigurace
Singleton Jedna instance, lazy loading Složitější pattern Globální konfigurace
Factory Flexibilní vytváření Extra abstrakce Komplexní objekty
Plain Object Jednoduché, framework agnostic Není OOP Malé utility
Pure Functions Nejjednodušší, testovatelné Žádná organizace Funkcionální přístup

Doporučení

  • Pro jednoduché transformace: Pure Functions nebo Statické metody
  • Pro flexibilní konfiguraci: Export třídy nebo Factory
  • Pro Vue 3 projekty: Composable
  • Pro globální použití: Export instance s setConfig

On gists

Vue parent - child

Popular ⭐ Vue.js

vue1.js #

// https://play.vuejs.org/#eNqdVE2P2jAQ/SsjXwCJBlXtic2itohDK7W72u7Rl5BMwItjW7bDUiH+e8d2krJbRNXlgOyZN1/Pb3Jkn43J9i2yOctdaYXx4NC3ZsGVaIy2Ho5gsZ5C6/ARGyMLjw9YwwlqqxsYUehogH7XVSE7RzaLt5CbAKVWzkMTLCH69lW68Si6RpMbrvJZ6oM6oIvvQHS7LywqD/nawoyut3//Bl++br3XCj6VUpS7W876ypk2qMYTzhZ3dEgN57OEvhpXSu0wBi7D6UJkqNz9b7suEh9EX5+Js+SYDWOxKfOO2KnFJntyWtE7HLkC4KzUjRES7Z3xgtjjbA7RE3yFlPr5W7R52+K0t5dbLHcX7E/uEGyc3Vt0aPfI2eDzhd2gT+7Vzx94oPPgpK5bSegrzgd0WrahxwT70qqK2j7DxW6/RokItXl0q4NH5fqhQqMBeYp4zkgwyyuj/2n3Q/YxxnF1IhYHtQUln6kGYLkVsoJxBEzeJp//VM0/xVKJPezfua1+JrRwIStJo5SFc2SI8KVWxBINukgq6u75jGJfSijuyZXdfbWr/TKmsrSKBBnXhXQ44apuVRk4hzRxoj0hs30hWyR8eDAg0s/Q3cAX4TF1wnNVYS0Urg4m4I+xyjRFw4nKv9x953/J9ITZOSW9FkxRVaSnOby32NzAuih3G6tJfXMwsm3oUxJQDelFqA7U6YXKxNTs9BvEAbCg

// Parent
<script setup>
import { ref, useTemplateRef } from 'vue'
import Modal from './Modal.vue'
const modalRef = useTemplateRef('modal');
</script>

<template>
Parent <br />
=================== <br />
<button @click="modalRef.open()">Open Modal</button>
<button @click="modalRef.close()">Close Modal</button>
<br /><br /><hr />
<Modal ref="modal" />
</template>

// Child - Modal
<template>
  Child (Modal) <br />
=================== <br />
<button @click="open()">Open Modal</button>
<button @click="close()">Close Modal</button>
<div v-show="isOpen" class="ModalContent">Modal Content</div>
</template>

<script setup>
import { ref } from 'vue'

const isOpen = ref(false)
function open() {
  isOpen.value = true 
}
function close() {
  isOpen.value = false 
}

defineExpose({ open, close })
</script>

<style>
  .ModalContent {
    padding: 1rem; background: plum;
    margin: 1rem;
  }
</style>

On gists

Vývoj softwaru / refactoring

Popular ⭐ Refactoring

readme.txt #

Vývoj software je často o hledání správné rovnováhy mezi:
čistotou kódu vs. praktičností
přehledností vs. flexibilitou
striktními pravidly vs. pragmatickým přístupem
A jak jste správně poznamenal - je to neustálý proces učení a přizpůsobování. Co fungovalo včera, nemusí být nejlepší řešení zítra. A to je v pořádku!
Důležité je:
Dělat informovaná rozhodnutí
Nebát se změny, když je potřeba
Učit se z předchozích zkušeností
Zachovat si zdravý rozum a neupnout se na jeden přístup
Proto je tak důležité mít v týmu otevřenou diskusi o těchto věcech - přesně jako teď! 👍

On gists

9 Smart Ways to Replace if-else in JavaScript

Popular ⭐ JavaScript

ways.js #

// https://medium.com/@hxu0407/9-smart-ways-to-replace-if-else-in-javascript-28f82ad6dcb9

// 1. Object Mapping Instead of if-else
function getPrice(user) {
  if (user.type === 'vip') {
    return 'VIP Price';
  } else if (user.type === 'svip') {
    return 'SVIP Price';
  } else if (user.type === 'vvip') {
    return 'VVIP Price';
  } else {
    return 'Regular Price';
  }
}

// better
const priceStrategy = {
  vip: () => 'VIP Price',
  svip: () => 'SVIP Price',
  vvip: () => 'VVIP Price',
  default: () => 'Regular Price'
};

function getPrice(user) {
  return (priceStrategy[user.type] || priceStrategy.default)();
}



// 2. Replace Multiple Conditions with Array.includes
if (status === 'failed' || status === 'error' || status === 'rejected') {
  handleError();
}

// better
const errorStatus = ['failed', 'error', 'rejected'];
if (errorStatus.includes(status)) {
  handleError();
}



// 3. Chained Ternary Operators
let message;
if (score >= 90) {
  message = 'Excellent';
} else if (score >= 80) {
  message = 'Good';
} else if (score >= 60) {
  message = 'Pass';
} else {
  message = 'Fail';
}

// better 
const message =
  score >= 90 ? 'Excellent' :
  score >= 80 ? 'Good' :
  score >= 60 ? 'Pass' :
  'Fail';
  
  
  
// 4. Logical Operators && and ||
// Replacing a simple `if`
user.isAdmin && showAdminPanel();
// Setting default values
const name = user.name || 'unnamed';
// Nullish coalescing
const count = data?.users ?? [];



// 5. Switch-Case with Pattern Matching
const actions = new Map([
  [/^vip/, handleVip],
  [/^admin/, handleAdmin],
  [/^user/, handleUser]
]);

/*or

const actions = [
  [/^vip/, handleVip],
  [/^admin/, handleAdmin],
  [/^user/, handleUser]
];
*/

const handleRequest = (type) => {
  const action = [...actions].find(([key]) => key.test(type));
  return action ? action[1]() : handleDefault();
};



// 6. Using Proxy for Conditional Interception
const handler = {
  get: (target, property) => {
    return property in target ? target[property] : target.default;
  }
};

const services = new Proxy({
  admin: () => 'Admin Service',
  user: () => 'User Service',
  default: () => 'Default Service'
}, handler);



// 7. Functional Approach
// Composing conditions
const isAdult = age => age >= 18;
const hasPermission = role => ['admin', 'superuser'].includes(role);
const canAccess = user => isAdult(user.age) && hasPermission(user.role);

// Usage
users.filter(canAccess).forEach(grantAccess);



// 8. State Machine Pattern
const stateMachine = {
  draft: {
    publish: 'published',
    delete: 'deleted'
  },
  published: {
    unpublish: 'draft',
    archive: 'archived'
  },
  archived: {
    restore: 'draft'
  }
};

const changeState = (currentState, action) =>
  stateMachine[currentState]?.[action] || currentState;
  
  
  
// 9. Use Decorators to Handle Conditional Logic
function checkPermission(target, name, descriptor) {
  const original = descriptor.value;
  descriptor.value = function (...args) {
    if (this.user?.hasPermission) {
      return original.apply(this, args);
    }
    throw new Error('No permission');
  };
  return descriptor;
}

class Document {
  @checkPermission
  edit() {
    // Edit the document
  }
}


On gists

Arbitrary props / variants / styling by parent

Popular ⭐ Tailwind CSS

demo.html #

<!-- https://play.tailwindcss.com/yyRSjWdDIv -->
<!--
https://tailwindcss.com/docs/adding-custom-styles#using-arbitrary-values
-->
<section class="m-5 *:mb-2 *:border *:text-red-400 [&_p]:text-blue-500">
  <div>text1</div>
  <div>text2</div>
  <div>text3</div>
  <div>text4</div>
  <div>text5</div>
  <p>Para 1</p>
  <p>Para 2</p>
  <div>text 6</div>
</section>


<div class="[&:nth-child(3)]:py-0">
  <!-- ... -->
</div>








<ul role="list" class="space-y-4 [&>*]:rounded-lg [&>*]:bg-white [&>*]:p-4 [&>*]:shadow">
  <li class="flex"> <!-- ... -->
</ul>


<ul
  role="list"
  class="space-y-4 [&>*]:rounded-lg [&>*]:bg-white [&>*]:p-4 [&>*]:shadow hover:[&>li:nth-child(2)>div>p:first-child]:text-indigo-500"
>
    
    
    
<ul class="m-5 [&>*:not(:last-child)]:text-green-500">
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
</ul


<ul class="m-5 [&>*:not(:last-child)]:after:content-[':)']">
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li> 
</ul>


<div class="[--scroll-offset:56px] lg:[--scroll-offset:44px]">

<div class="[mask-type:luminance] hover:[mask-type:alpha]">
  
<div class="bg-[url('/what_a_rush.png')]">
  
  
  
When using arbitrary values, Tailwind can generally handle this ambiguity automatically based on the value you pass in:

<!-- Will generate a font-size utility -->
<div class="text-[22px]">...</div>

<!-- Will generate a color utility -->
<div class="text-[#bada55]">...</div>
Sometimes it really is ambiguous though, for example when using CSS variables:

<div class="text-[var(--my-var)]">...</div>
In these situations, you can “hint” the underlying type to Tailwind by adding a CSS data type before the value:

<!-- Will generate a font-size utility -->
<div class="text-[length:var(--my-var)]">...</div>

<!-- Will generate a color utility -->
<div class="text-[color:var(--my-var)]">...</div>





<!--
  https://play.tailwindcss.com/sLrFGm1VtG
--


<!-- Via class, ale muze to chytnout i vyssi tridu, takze radsi jeste dat #ID na ten element a pres nej ve smyslu [#nejakeId.theme-light_&] -->
<section class="theme-light">
  <article>
    <div class="[.theme-light_&]:bg-red-200 [.theme-dark_&]:bg-gray-800">
      Obsah
    </div>
  </article>
</section>



<!-- Přes data atribut -->
<div data-theme="dark" class="[&[data-theme='light']]:bg-red-200 [&[data-theme='dark']]:bg-gray-800">
  Obsah
</div>

<!-- Nebo pro parent selector -->
<section data-theme="light">
  <div class="[[data-theme='light']_&]:bg-red-200 [[data-theme='dark']_&]:bg-gray-800">
    Obsah
  </div>
</section>


<!--

:root {
  --x: pink;
}
-->
<div class="hover:bg-[--x]"> kuku</div>

<!-- group + class -->
<div class="card border flex justify-center items-center group card--danger bg-orange-600">
  <a href="#" class="text-black group-[.card--danger]:text-white">Danger Card</a>
</div>

On gists

Ben Hong - One object to rule them all

Popular ⭐ Vue.js

obj.vue #

<!-- https://www.vuemastery.com/courses/component-design-patterns/one-object-to-rule-them-all -->

<!-- To heavy , to complicated, all props we dont need ...-->
<template>
  <main>
    <Component 
      v-for="content in apiResponse"
      :key="content.id"
      :is="content.type"
      :article-title="content.title"
      :article-content="content.body"
      :ad-image="content.image"
      :ad-heading="content.heading"
      @click="content.type === 'NewsArticle' ? openArticle : openAd"
      @mouseover="content.type === 'NewsArticle' ? showPreview : trackAdEvent"
    />
  </main>
</template>


<!--Much better -->
<template>
  <main>
    <Component 
      v-for="content in apiResponse"
      :key="content.id"
      :is="content.type"
      v-bind="feedItem(content).attrs"
      v-on="feedItem(content).events"
    />
  </main>
</template>

<script>
export default {
  methods: {
    feedItem(item) {
      if (item.type === 'NewsArticle') {
        return {
          attrs: {
            'article-title': item.title,
            'article-content': item.content
          },
          events: {
            click: this.openArticle,
            mouseover: this.showPreview
          }
        }
      } else if (item.type === 'NewsAd') {
        return {
          attrs: {
            'ad-image': item.image,
            'ad-heading': item.heading
          },
          events: {
            click: this.openAd,
            mouseover: this.trackAdEvent
          }
        }
      }
    }
  }
}
</script>



<!-- The best! -->
<script>
export default {
  computed: {
    feedItems() {
      return this.apiResponse.map(item => {
        if (item.type === 'NewsArticle') {
          return {
            id: item.id,
            type: item.type,
            attrs: {
              'article-title': item.title,
              'article-content': item.content,
              class: 'feed-item feed-item--article'
            },
            events: {
              click: this.openArticle,
              mouseover: this.showPreview
            }
          }
        } else if (item.type === 'NewsAd') {
          return {
            id: item.id,
            type: item.type,
            attrs: {
              'ad-image': item.image,
              'ad-heading': item.heading,
              class: 'feed-item feed-item--ad'
            },
            events: {
              click: this.openAd,
              mouseover: this.trackAdEvent
            }
          }
        }
      })
    }
  }
}

// OR
computed: {
  feedItems() {
    return this.apiResponse.map(item => ({
      ...this.getFeedItemConfig(item),
      id: item.id
    }))
  }
},
methods: {
  getFeedItemConfig(item) {
    // Tohle se volá jen v computed, ne při každém renderu
    if (item.type === 'NewsArticle') { /* ... */ }
  }
}


</script