# Personal widgets

> Small widgets for a personal site: availability badge, local clock with working-hours state, social links, now list, stats, skills and weather.

Source: https://docs.nasaqui.com/components/personal-widgets

## Install

```bash
npx shadcn@latest add https://docs.nasaqui.com/r/personal-widgets.json
```

Small, self-contained blocks for a personal site or profile. They are presentational: none of them fetches anything. Pass the data you have.

| Widget | What it shows |
| --- | --- |
| `AvailabilityBadge` | "Available for work" with a status dot. `open`, `limited`, `closed`. |
| `LocalClock` | The owner's local time, the difference from the visitor's zone, and whether it is working hours there. |
| `SocialLinks` | Links as chips or a list. GitHub shows its official mark; other brands are text. |
| `NowWidget` | What I am doing now: building, reading, learning. |
| `StatsWidget` | A few numbers (years, projects, stars), compact notation optional. |
| `SkillsWidget` | Skills grouped by area with a five-dot level, also in text. |
| `WeatherWidget` | Temperature, condition, high and low, in Celsius or Fahrenheit. |

## When to use

- The side column of a profile, a footer, a link-in-bio page.

## When not to use

- Live weather or analytics: fetch elsewhere and pass values in.

## Import

```tsx
import { LocalClock, SocialLinks, StatsWidget } from "@fadymondy/nasaq/web";
```

## Quick start

```tsx
<LocalClock timeZone="Asia/Riyadh" city="Riyadh" />
<StatsWidget stats={[{ label: "Projects", value: 42 }, { label: "Stars", value: 12400, compact: true }]} />
```

## Anatomy

Each widget is a `Card` (or a list, for `SocialLinks` and `SkillsWidget`) with a `data-slot` named after it.

## API

- `AvailabilityBadge`: `status`, `note?`, `labels?`.
- `LocalClock`: `timeZone`, `city?`, `viewerTimeZone?`, `workingHours?` (default Sunday to Thursday, 9 to 17), `now?`, `labels?`. Updates every 15 seconds.
- `SocialLinks`: `links` (`{ kind, label, href, handle? }`), `layout?` (`chips` or `list`).
- `NowWidget`: `items` (`{ label, text, href? }`), `updated?`, `title?`.
- `StatsWidget`: `stats` (`{ label, value, suffix?, compact? }`), `title?`.
- `SkillsWidget`: `skills` (`{ name, group?, level? }`), `levels?`.
- `WeatherWidget`: `city`, `temperature` (Celsius), `condition`, `high?`, `low?`, `unit?`.
- All take `labels?: Partial<PersonalWidgetLabels>`.

## Accessibility

- The clock is a `<time>` with a text description of the working state.
- Skill level is spoken as text, not only dots.
- Weather condition is text; the icon is hidden from assistive tech.

## RTL & i18n

English and Arabic strings. Times, temperatures and numbers use Latin digits. Handles are always left to right.

## Styling

Tokens only; status colors come from the badge variants.

## Do / Don't

- Do pass a real IANA zone.
- Don't add brand icons for networks other than GitHub; the official-marks rule applies.

## Related

[`ProfilePage`](https://docs.nasaqui.com/components/profile-page), [`Badge`](https://docs.nasaqui.com/components/badge), [`Card`](https://docs.nasaqui.com/components/card).

## Lab

Story `Components/Brand/Personal Widgets`.

## Code

### React

```tsx
<LocalClock timeZone="Asia/Riyadh" city="Riyadh" />
<StatsWidget stats={[{ label: "Projects", value: 42 }, { label: "Stars", value: 12400, compact: true }]} />
```

### shadcn

```tsx
<LocalClock timeZone="Asia/Riyadh" city="Riyadh" />
<StatsWidget stats={[{ label: "Projects", value: 42 }, { label: "Stars", value: 12400, compact: true }]} />
```

### Vue

```vue
<script setup lang="ts">
import { NqAvailabilityBadge, NqLocalClock, NqNowWidget, NqSkillsWidget, NqSocialLinks, NqStatsWidget, NqWeatherWidget } from "@fadymondy/nasaq/vue";
</script>

<template>
  <div class="grid max-w-3xl gap-4 sm:grid-cols-2">
    <NqAvailabilityBadge status="open" note="from November" />
    <NqLocalClock time-zone="Asia/Riyadh" city="Riyadh" :working-hours="{ start: 9, end: 17, days: [0, 1, 2, 3, 4] }" />
    <NqStatsWidget :stats="[{ label: 'Projects', value: 42 }, { label: 'Stars', value: 12400, compact: true }]" />
    <NqWeatherWidget city="Riyadh" :temperature="38" condition="clear" :high="41" :low="27" />
    <NqNowWidget :items="[{ label: 'Building', text: 'A design system' }, { label: 'Reading', text: 'Refactoring UI', href: 'https://example.com' }]" updated="2026-09-20" />
    <NqSkillsWidget :skills="[{ name: 'TypeScript', group: 'Languages', level: 5 }, { name: 'Go', group: 'Languages', level: 3 }, { name: 'Figma', level: 4 }]" />
    <NqSocialLinks :links="[{ kind: 'github', label: 'GitHub', href: 'https://github.com/fadymondy', handle: '@fadymondy' }, { kind: 'email', label: 'Email', href: 'mailto:hi@example.com' }]" />
  </div>
</template>
```

### Blade

```blade
<div class="grid max-w-3xl gap-4 sm:grid-cols-2">
    <x-nq::personal-widgets.availability-badge status="open" note="from November" />
    <x-nq::personal-widgets.local-clock time-zone="Asia/Riyadh" viewer-time-zone="UTC" city="Riyadh" :now="'2026-09-29T09:00:00Z'" :working-hours="['start' => 9, 'end' => 17, 'days' => [0, 1, 2, 3, 4]]" />
    <x-nq::personal-widgets.stats-widget :stats="[['label' => 'Projects', 'value' => 42], ['label' => 'Stars', 'value' => 12400, 'compact' => true, 'suffix' => '+']]" />
    <x-nq::personal-widgets.weather-widget city="Riyadh" :temperature="38" condition="clear" :high="41" :low="27" />
    <x-nq::personal-widgets.now-widget :items="[['label' => 'Building', 'text' => 'A design system'], ['label' => 'Reading', 'text' => 'Refactoring UI', 'href' => 'https://example.com']]" updated="2026-09-20" />
    <x-nq::personal-widgets.skills-widget :skills="[['name' => 'TypeScript', 'group' => 'Languages', 'level' => 5], ['name' => 'Go', 'group' => 'Languages', 'level' => 3], ['name' => 'Figma', 'level' => 4]]" />
    <x-nq::personal-widgets.social-links layout="list" :links="[['kind' => 'github', 'label' => 'GitHub', 'href' => 'https://github.com/fadymondy', 'handle' => '@fadymondy'], ['kind' => 'email', 'label' => 'Email', 'href' => 'mailto:hi@example.com']]" />
</div>
```

### HTML + Alpine

```html
<div class="grid max-w-3xl gap-4 sm:grid-cols-2">
    <span data-slot="availability-badge" data-status="open"
    class="inline-flex h-6 shrink-0 items-center gap-1.5 whitespace-nowrap rounded-[4px] border px-2 text-body-sm font-medium [&_svg]:size-3 border-nq-success/40 bg-nq-success-soft text-nq-success-text">
    <span aria-hidden="true" class="size-1.5 rounded-full bg-current motion-safe:animate-pulse"></span>
    Available for work
    <span class="font-normal opacity-80">from November</span></span>
    <div data-slot="local-clock" x-data="nqLocalClock(JSON.parse('{\u0022timeZone\u0022:\u0022Asia\\\/Riyadh\u0022,\u0022viewerTimeZone\u0022:\u0022UTC\u0022,\u0022workingHours\u0022:{\u0022start\u0022:9,\u0022end\u0022:17,\u0022days\u0022:[0,1,2,3,4]},\u0022locale\u0022:\u0022en\u0022,\u0022now\u0022:1790672400000,\u0022labels\u0022:{\u0022working\u0022:\u0022Working hours\u0022,\u0022offHours\u0022:\u0022Outside working hours\u0022,\u0022sameTime\u0022:\u0022Same time as you\u0022,\u0022ahead\u0022:\u0022{n}h ahead of you\u0022,\u0022behind\u0022:\u0022{n}h behind you\u0022}}'))"
    class="flex flex-col gap-2 rounded-card border border-border bg-card py-4 text-card-foreground">
    <div data-slot="card-header" class="grid auto-rows-min items-start gap-1 px-4"><div data-slot="card-title" class="text-label text-foreground">Local time</div></div>
    <div data-slot="card-content" class="flex flex-col gap-1 px-4">
        <p class="flex items-baseline gap-2">
            <time x-bind:datetime="iso" x-text="time" datetime="2026-09-29T09:00:00+00:00" class="text-h1 tabular-nums text-foreground" dir="ltr">12:00 PM</time>
            <span class="text-body-sm text-muted-foreground">in Riyadh</span>        </p>
        <p class="flex items-center gap-1.5 text-body-sm text-muted-foreground">
            <span aria-hidden="true" x-bind:class="working ? 'bg-nq-success' : 'bg-nq-line-strong'" class="size-2 rounded-full bg-nq-success"></span>
            <span x-text="statusText">Working hours</span>
        </p>
        <p class="text-caption text-muted-foreground" x-text="offsetText">3h ahead of you</p>
    </div>
</div>
    <div data-slot="stats-widget" class="flex flex-col gap-3 rounded-card border border-border bg-card py-4 text-card-foreground">
    <div data-slot="card-header" class="grid auto-rows-min items-start gap-1 px-4"><div data-slot="card-title" class="text-label text-foreground">By the numbers</div></div>
    <div data-slot="card-content" class="px-4">
        <dl class="grid grid-cols-2 gap-x-4 gap-y-3">
                                            <div class="flex flex-col">
                    <dd class="order-first text-h1 text-foreground"><bdi data-slot="num" data-numeric="" class="tabular-nums">42</bdi>
</dd>
                    <dt class="text-caption text-muted-foreground">Projects</dt>
                </div>
                                            <div class="flex flex-col">
                    <dd class="order-first text-h1 text-foreground"><bdi data-slot="num" data-numeric="" class="tabular-nums">12K</bdi>
+</dd>
                    <dt class="text-caption text-muted-foreground">Stars</dt>
                </div>
                    </dl>
    </div>
</div>
    <div data-slot="weather-widget" data-condition="clear" class="flex flex-col gap-2 rounded-card border border-border bg-card py-4 text-card-foreground">
    <div data-slot="card-header" class="grid auto-rows-min items-start gap-1 px-4"><div data-slot="card-title" class="text-label text-foreground">Weather</div></div>
    <div data-slot="card-content" class="flex items-center gap-3 px-4">
        <svg data-slot="icon" aria-hidden="true" class="size-9 shrink-0 text-nq-accent-text" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <circle cx="12" cy="12" r="4"/>
  <path d="M12 2v2"/>
  <path d="M12 20v2"/>
  <path d="m4.93 4.93 1.41 1.41"/>
  <path d="m17.66 17.66 1.41 1.41"/>
  <path d="M2 12h2"/>
  <path d="M20 12h2"/>
  <path d="m6.34 17.66-1.41 1.41"/>
  <path d="m19.07 4.93-1.41 1.41"/>
</svg>        <div class="flex min-w-0 flex-col">
            <p class="text-h1 text-foreground" dir="ltr">38°C</p>
            <p class="text-body-sm text-muted-foreground">Clear, <bdi>Riyadh</bdi></p>
            <p class="text-caption text-muted-foreground">High 41°C, low 27°C</p>        </div>
    </div>
</div>
    <div data-slot="now-widget" class="flex flex-col gap-3 rounded-card border border-border bg-card py-4 text-card-foreground">
    <div data-slot="card-header" class="grid auto-rows-min items-start gap-1 px-4">
        <div data-slot="card-title" class="text-label text-foreground">Now</div>
        <p class="text-caption text-muted-foreground">Updated Sep 20, 2026</p>    </div>
    <div data-slot="card-content" class="px-4">
        <dl class="flex flex-col gap-2.5">
                                            <div class="flex flex-col gap-0.5">
                    <dt class="text-caption text-muted-foreground">Building</dt>
                    <dd dir="auto" class="text-body-sm text-foreground">
                        A design system                    </dd>
                </div>
                                            <div class="flex flex-col gap-0.5">
                    <dt class="text-caption text-muted-foreground">Reading</dt>
                    <dd dir="auto" class="text-body-sm text-foreground">
                        <a href="https://example.com" class="underline decoration-nq-line-strong underline-offset-4 hover:decoration-current">Refactoring UI</a>                    </dd>
                </div>
                    </dl>
    </div>
</div>
    <div data-slot="skills-widget" class="flex flex-col gap-5">
            <div class="flex flex-col gap-2">
            <p class="eyebrow">Languages</p>            <ul class="flex flex-wrap gap-2">
                                    <li class="inline-flex h-control-sm items-center gap-2 rounded-control border border-border bg-card px-2.5 text-body-sm text-foreground">
                        <bdi>TypeScript</bdi>
                                                    <span role="img" aria-label="Level 5 of 5" class="flex gap-0.5">
                                <span class="size-1.5 rounded-full bg-primary"></span><span class="size-1.5 rounded-full bg-primary"></span><span class="size-1.5 rounded-full bg-primary"></span><span class="size-1.5 rounded-full bg-primary"></span><span class="size-1.5 rounded-full bg-primary"></span>                            </span>
                                            </li>
                                    <li class="inline-flex h-control-sm items-center gap-2 rounded-control border border-border bg-card px-2.5 text-body-sm text-foreground">
                        <bdi>Go</bdi>
                                                    <span role="img" aria-label="Level 3 of 5" class="flex gap-0.5">
                                <span class="size-1.5 rounded-full bg-primary"></span><span class="size-1.5 rounded-full bg-primary"></span><span class="size-1.5 rounded-full bg-primary"></span><span class="size-1.5 rounded-full bg-nq-line"></span><span class="size-1.5 rounded-full bg-nq-line"></span>                            </span>
                                            </li>
                            </ul>
        </div>
            <div class="flex flex-col gap-2">
                        <ul class="flex flex-wrap gap-2">
                                    <li class="inline-flex h-control-sm items-center gap-2 rounded-control border border-border bg-card px-2.5 text-body-sm text-foreground">
                        <bdi>Figma</bdi>
                                                    <span role="img" aria-label="Level 4 of 5" class="flex gap-0.5">
                                <span class="size-1.5 rounded-full bg-primary"></span><span class="size-1.5 rounded-full bg-primary"></span><span class="size-1.5 rounded-full bg-primary"></span><span class="size-1.5 rounded-full bg-primary"></span><span class="size-1.5 rounded-full bg-nq-line"></span>                            </span>
                                            </li>
                            </ul>
        </div>
    </div>
    <ul data-slot="social-links" class="flex list-none gap-2 p-0 flex-col">
                    <li>
            <a href="https://github.com/fadymondy"  target="_blank" rel="noopener noreferrer"  class="inline-flex items-center gap-2 rounded-control text-body-sm text-foreground outline-none transition-colors duration-150 ease-nq focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus py-1 hover:underline hover:decoration-nq-line-strong hover:underline-offset-4">
                <svg viewBox="0 0 98 96" width="20" height="20" aria-hidden="true" focusable="false" class="size-4">
    <path d="M41.4395 69.3848C28.8066 67.8535 19.9062 58.7617 19.9062 46.9902C19.9062 42.2051 21.6289 37.0371 24.5 33.5918C23.2559 30.4336 23.4473 23.7344 24.8828 20.959C28.7109 20.4805 33.8789 22.4902 36.9414 25.2656C40.5781 24.1172 44.4062 23.543 49.0957 23.543C53.7852 23.543 57.6133 24.1172 61.0586 25.1699C64.0254 22.4902 69.2891 20.4805 73.1172 20.959C74.457 23.543 74.6484 30.2422 73.4043 33.4961C76.4668 37.1328 78.0937 42.0137 78.0937 46.9902C78.0937 58.7617 69.1934 67.6621 56.3691 69.2891C59.623 71.3945 61.8242 75.9883 61.8242 81.252L61.8242 91.2051C61.8242 94.0762 64.2168 95.7031 67.0879 94.5547C84.4102 87.9512 98 70.6289 98 49.1914C98 22.1074 75.9883 6.69539e-07 48.9043 4.309e-07C21.8203 1.92261e-07 -1.9479e-07 22.1074 -4.3343e-07 49.1914C-6.20631e-07 70.4375 13.4941 88.0469 31.6777 94.6504C34.2617 95.6074 36.75 93.8848 36.75 91.3008L36.75 83.6445C35.4102 84.2188 33.6875 84.6016 32.1562 84.6016C25.8398 84.6016 22.1074 81.1563 19.4277 74.7441C18.375 72.1602 17.2266 70.6289 15.0254 70.3418C13.877 70.2461 13.4941 69.7676 13.4941 69.1934C13.4941 68.0449 15.4082 67.1836 17.3223 67.1836C20.0977 67.1836 22.4902 68.9063 24.9785 72.4473C26.8926 75.2227 28.9023 76.4668 31.2949 76.4668C33.6875 76.4668 35.2187 75.6055 37.4199 73.4043C39.0469 71.7773 40.291 70.3418 41.4395 69.3848Z"  class="fill-black dark:fill-white"  />
</svg>
                                                <span>GitHub</span>
                <span dir="ltr" class="text-muted-foreground">@fadymondy</span>            </a>
        </li>
                    <li>
            <a href="mailto:hi@example.com"  class="inline-flex items-center gap-2 rounded-control text-body-sm text-foreground outline-none transition-colors duration-150 ease-nq focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus py-1 hover:underline hover:decoration-nq-line-strong hover:underline-offset-4">
                                <svg data-slot="icon" aria-hidden="true" class="size-4 text-muted-foreground" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7"/>
  <rect x="2" y="4" width="20" height="16" rx="2"/>
</svg>                                <span>Email</span>
                            </a>
        </li>
    </ul>
</div>
```
