Skip to content

Commit 254d03a

Browse files
authored
Merge pull request #128 from teritorio/develop
Release v1.2.0
2 parents e64a034 + ae6f869 commit 254d03a

File tree

85 files changed

+795
-1609
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+795
-1609
lines changed

.github/workflows/ci.yml

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ name: CI
33
on:
44
pull_request:
55
branches: [develop, main]
6-
push:
7-
branches: [develop]
86

97
jobs:
108
ci:
@@ -30,3 +28,96 @@ jobs:
3028

3129
- name: Test
3230
run: pnpm test:run
31+
32+
lighthouse:
33+
runs-on: ubuntu-latest
34+
permissions:
35+
issues: write
36+
pull-requests: write
37+
steps:
38+
- uses: actions/checkout@v6
39+
40+
- uses: pnpm/action-setup@v5
41+
42+
- uses: actions/setup-node@v6
43+
with:
44+
node-version: 22
45+
cache: pnpm
46+
47+
- name: Install dependencies
48+
run: pnpm install --frozen-lockfile
49+
50+
# No NUXT_APP_BASE_URL here — Lighthouse audits the site at root,
51+
# unlike deploy.yml which prefixes /clearance-website/ for GitHub Pages.
52+
- name: Generate static site
53+
run: pnpm generate
54+
55+
- name: Lighthouse CI
56+
id: lhci
57+
uses: treosh/lighthouse-ci-action@v12
58+
if: always()
59+
with:
60+
configPath: ./lighthouserc.cjs
61+
62+
- name: Comment Lighthouse results on PR
63+
if: always() && github.event_name == 'pull_request' && steps.lhci.outputs.resultsPath != ''
64+
env:
65+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
66+
RESULTS_PATH: ${{ steps.lhci.outputs.resultsPath }}
67+
LINKS: ${{ steps.lhci.outputs.links }}
68+
run: |
69+
MANIFEST="$RESULTS_PATH/manifest.json"
70+
if [ ! -f "$MANIFEST" ]; then exit 0; fi
71+
72+
COMMENT=$(jq -r '
73+
"## Lighthouse Report\n\n| URL | Performance | Accessibility | Best Practices | SEO |\n|-----|:-----------:|:------------:|:--------------:|:---:|",
74+
(
75+
[ .[] | select(.isRepresentativeRun == true) ] |
76+
.[] |
77+
"| `\(.url | split("//")[1] | split("/")[1:] | "/" + join("/"))` | \((.summary.performance // 0) * 100 | round) | \((.summary.accessibility // 0) * 100 | round) | \((.summary["best-practices"] // 0) * 100 | round) | \((.summary.seo // 0) * 100 | round) |"
78+
)
79+
' "$MANIFEST")
80+
81+
if [ -n "$LINKS" ]; then
82+
COMMENT+=$'\n\n'"**Report links:** ${LINKS}"
83+
fi
84+
85+
echo "$COMMENT" | gh pr comment "${{ github.event.pull_request.number }}" --body-file -
86+
87+
- name: Create issue on Lighthouse regression
88+
if: always() && steps.lhci.outcome == 'failure' && steps.lhci.outputs.resultsPath != ''
89+
env:
90+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
91+
RESULTS_PATH: ${{ steps.lhci.outputs.resultsPath }}
92+
LINKS: ${{ steps.lhci.outputs.links }}
93+
run: |
94+
if [ ! -f "$RESULTS_PATH/assertion-results.json" ]; then
95+
echo "No assertion results found — LHCI likely crashed during collection."
96+
exit 0
97+
fi
98+
99+
REPORT=$(jq -r '
100+
.[] | select(.level == "error" or .level == "warn") |
101+
"- **\(.level)**: \(.auditId // .name) — score \(.actual) (expected ≥ \(.expected))"
102+
' "$RESULTS_PATH/assertion-results.json")
103+
104+
gh issue create \
105+
--title "Lighthouse regression detected on $(date +%Y-%m-%d)" \
106+
--label "lighthouse" \
107+
--assignee wazolab \
108+
--body "$(cat <<EOF
109+
## Lighthouse CI failed
110+
111+
**Run:** ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
112+
**Commit:** \`${{ github.sha }}\`
113+
**Branch:** \`${{ github.ref_name }}\`
114+
115+
### Failures
116+
117+
${REPORT}
118+
119+
### Report links
120+
121+
${LINKS}
122+
EOF
123+
)"

CLAUDE.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ Clearance Website — marketing/presentational site for Clearance, an open-sourc
2626
- ESLint: `@antfu/eslint-config`
2727
- Commits: conventional commits enforced by commitlint
2828
- Git hooks: simple-git-hooks + lint-staged
29+
- **Mobile-first**: Design and implement for mobile screens first, then enhance for larger breakpoints
30+
31+
## Tests
32+
33+
- **Keep tests in sync with the codebase.** Every time an issue is resolved, verify that existing tests still match the code and update or remove tests that no longer apply. Add new tests for new functionality.
2934

3035
## Git Commits
3136

components/AppFooter.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const footerColumns = computed(() => [
3131
<UFooter :ui="{ top: 'border-b border-t border-default' }">
3232
<template #top>
3333
<UContainer>
34-
<UFooterColumns :columns="footerColumns" :ui="{ root: 'block!', left: 'hidden!', center: 'grid! grid-cols-3! gap-8' }" />
34+
<UFooterColumns :columns="footerColumns" :ui="{ root: 'block!', left: 'hidden!', center: 'grid! grid-cols-1! sm:grid-cols-3! gap-8' }" />
3535
</UContainer>
3636
</template>
3737
<template #left>

components/AppHeader.vue

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
<script setup lang="ts">
2-
const { t, locale, locales } = useI18n()
3-
const switchLocalePath = useSwitchLocalePath()
2+
const APP_URL = 'https://clearance.teritorio.xyz'
3+
4+
const { t } = useI18n()
45
const localePath = useLocalePath()
6+
const drawerOpen = ref(false)
57
68
const navItems = computed(() => [
79
{
@@ -21,33 +23,39 @@ const navItems = computed(() => [
2123
target: '_blank',
2224
},
2325
])
24-
25-
const localeItems = computed(() =>
26-
(locales.value as Array<{ code: string, name: string }>).map(l => ({
27-
label: l.name,
28-
to: switchLocalePath(l.code as typeof locale.value),
29-
})),
30-
)
3126
</script>
3227

3328
<template>
34-
<UHeader :to="localePath('/')">
29+
<UHeader v-model:open="drawerOpen" :to="localePath('/')" mode="drawer">
3530
<template #title>
3631
<div class="flex items-center gap-2">
3732
<NuxtImg src="/logo.svg" alt="Clearance" width="28" height="28" />
3833
<span>{{ t('nav.home') }}</span>
3934
</div>
4035
</template>
4136
<template #right>
42-
<UNavigationMenu :items="navItems" />
43-
<UDropdownMenu :items="localeItems">
44-
<UButton
45-
variant="ghost"
46-
icon="i-lucide-languages"
47-
:label="locale.toUpperCase()"
48-
:aria-label="t('nav.changeLanguage')"
49-
/>
50-
</UDropdownMenu>
37+
<UNavigationMenu class="hidden md:flex" :items="navItems" />
38+
<UButton
39+
:label="t('nav.openApp')"
40+
:to="APP_URL"
41+
target="_blank"
42+
rel="noopener"
43+
icon="i-lucide-external-link"
44+
class="hidden md:flex"
45+
/>
46+
<AppLanguageSwitcher />
47+
</template>
48+
<template #body>
49+
<UNavigationMenu orientation="vertical" :items="navItems" />
50+
<UButton
51+
:label="t('nav.openApp')"
52+
:to="APP_URL"
53+
target="_blank"
54+
rel="noopener"
55+
icon="i-lucide-external-link"
56+
class="mt-4 w-full justify-center"
57+
@click="drawerOpen = false"
58+
/>
5159
</template>
5260
</UHeader>
5361
</template>

components/AppLanguageSwitcher.vue

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<script setup lang="ts">
2+
const { t, locale, locales } = useI18n()
3+
const switchLocalePath = useSwitchLocalePath()
4+
5+
const localeItems = computed(() =>
6+
(locales.value as Array<{ code: string, name: string }>).map(l => ({
7+
label: l.name,
8+
to: switchLocalePath(l.code as typeof locale.value),
9+
})),
10+
)
11+
</script>
12+
13+
<template>
14+
<UDropdownMenu :items="localeItems">
15+
<UButton
16+
variant="ghost"
17+
icon="i-lucide-languages"
18+
:label="locale.toUpperCase()"
19+
:aria-label="t('nav.changeLanguage')"
20+
/>
21+
</UDropdownMenu>
22+
</template>

components/content/LandingFeature.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ defineProps<{
66
</script>
77

88
<template>
9-
<div class="rounded-xl border border-zinc-200 bg-zinc-50 p-6">
9+
<div class="rounded-xl border border-zinc-200 bg-zinc-50 p-4 sm:p-6">
1010
<div v-if="icon" class="mb-4 flex size-10 items-center justify-center rounded-lg bg-primary/10">
1111
<UIcon :name="icon" class="size-5 text-primary" />
1212
</div>

components/content/LandingProblemCard.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ defineProps<{
55
</script>
66

77
<template>
8-
<div class="landing-problem-card rounded-xl border-2 border-primary/30 bg-zinc-50 p-6 text-center">
8+
<div class="landing-problem-card rounded-xl border-2 border-primary/30 bg-zinc-50 p-4 sm:p-6 text-center">
99
<div v-if="icon" class="mb-4 mx-auto flex size-10 items-center justify-center rounded-lg bg-primary/10">
1010
<UIcon :name="icon" class="size-5 text-primary" />
1111
</div>

components/content/LandingReference.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ defineProps<{
66
</script>
77

88
<template>
9-
<div class="flex flex-col rounded-xl border border-zinc-200 bg-white p-6">
9+
<div class="flex flex-col rounded-xl border border-zinc-200 bg-white p-4 sm:p-6">
1010
<div class="mb-3 flex items-center gap-3">
11-
<img v-if="logo" :src="logo" :alt="title" class="h-16 w-28 shrink-0 object-contain">
11+
<NuxtImg v-if="logo" :src="logo" :alt="title" class="h-12 w-20 sm:h-16 sm:w-28 shrink-0 object-contain" />
1212
<h3 class="text-base font-semibold">
1313
{{ title }}
1414
</h3>

components/content/LandingSchema.vue

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,45 @@ defineProps<{
1010

1111
<template>
1212
<div class="mx-auto max-w-3xl">
13+
<!-- Mobile: simplified vertical flow -->
14+
<div class="flex flex-col items-center gap-3 sm:hidden" role="img" :aria-label="`${changesLabel} → ${osmLabel} → ${clearanceLabel} → ${extractLabel}`">
15+
<div class="flex w-full max-w-xs items-center gap-3 rounded-xl border border-zinc-200 bg-zinc-50 px-4 py-3">
16+
<div class="flex size-10 shrink-0 items-center justify-center rounded-full border-2 border-zinc-300 bg-white">
17+
<svg viewBox="0 0 30 14" class="h-3.5 w-[30px]" aria-hidden="true"><path d="M 0,7 Q 5,0 10,7 Q 15,14 20,7 Q 25,0 30,7" fill="none" stroke="#3f3f46" stroke-width="2.2" stroke-linecap="round" /></svg>
18+
</div>
19+
<span class="text-sm text-muted">{{ changesLabel }}</span>
20+
</div>
21+
<UIcon name="i-lucide-chevron-down" class="size-5 text-primary" />
22+
<div class="flex w-full max-w-xs items-center gap-3 rounded-xl border border-zinc-200 bg-zinc-50 px-4 py-3">
23+
<div class="flex size-10 shrink-0 items-center justify-center rounded-full border-2 border-zinc-300 bg-white">
24+
<NuxtImg src="/logos/openstreetmap.png" alt="OpenStreetMap" class="size-6 object-contain" />
25+
</div>
26+
<span class="text-sm text-muted">{{ osmLabel }}</span>
27+
</div>
28+
<UIcon name="i-lucide-chevron-down" class="size-5 text-primary" />
29+
<div class="flex w-full max-w-xs items-center gap-3 rounded-xl border-2 border-primary/30 bg-primary/5 px-4 py-3">
30+
<div class="flex size-10 shrink-0 items-center justify-center rounded-full border-2 border-primary bg-white">
31+
<svg viewBox="0 0 24 24" class="size-6" aria-hidden="true"><circle cx="10" cy="10" r="8" fill="#ffbb00" stroke="#000" stroke-width="0.8" /><circle cx="15" cy="15" r="6.5" fill="#f00" stroke="#000" stroke-width="0.8" /></svg>
32+
</div>
33+
<span class="text-sm font-medium">{{ clearanceLabel }}</span>
34+
</div>
35+
<UIcon name="i-lucide-chevron-down" class="size-5 text-primary" />
36+
<div class="flex w-full max-w-xs items-center gap-3 rounded-xl border border-zinc-200 bg-zinc-50 px-4 py-3">
37+
<div class="flex size-10 shrink-0 items-center justify-center rounded-full border-2 border-zinc-300 bg-white">
38+
<UIcon name="i-lucide-database" class="size-5 text-zinc-700" />
39+
</div>
40+
<span class="text-sm text-muted">{{ extractLabel }}</span>
41+
</div>
42+
<div class="mt-1 flex items-center gap-1 text-xs text-muted italic">
43+
<UIcon name="i-lucide-rotate-ccw" class="size-3.5" />
44+
<span>{{ feedbackLabel }}</span>
45+
</div>
46+
</div>
47+
48+
<!-- Desktop: full SVG diagram -->
1349
<svg
1450
viewBox="0 -15 560 170"
15-
class="block w-full"
51+
class="hidden w-full sm:block"
1652
role="img"
1753
:aria-label="`${changesLabel} → ${osmLabel} → ${clearanceLabel} → ${extractLabel}`"
1854
>

components/content/LandingStep.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ defineProps<{
2323
</div>
2424

2525
<!-- Card -->
26-
<div class="flex h-full flex-col items-center rounded-xl border border-zinc-200 bg-white px-6 pb-6 pt-8 text-center shadow-sm">
26+
<div class="flex h-full flex-col items-center rounded-xl border border-zinc-200 bg-white px-4 pb-4 sm:px-6 sm:pb-6 pt-8 text-center shadow-sm">
2727
<h3 class="text-lg font-semibold">
2828
{{ title }}
2929
</h3>
@@ -36,7 +36,7 @@ defineProps<{
3636

3737
<style scoped>
3838
/* Horizontal chevron arrow between steps (desktop) */
39-
@media (min-width: 640px) {
39+
@media (min-width: 768px) {
4040
.landing-step:not(:last-child)::after {
4141
content: '';
4242
position: absolute;
@@ -51,7 +51,7 @@ defineProps<{
5151
}
5252
5353
/* Vertical chevron arrow between steps (mobile) */
54-
@media (max-width: 639px) {
54+
@media (max-width: 767px) {
5555
.landing-step:not(:last-child)::after {
5656
content: '';
5757
position: absolute;

0 commit comments

Comments
 (0)