From 0c28be112361452f2dfadbe517eeff920c4f4f28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Chopin?= Date: Wed, 21 Oct 2026 19:07:34 +0200 Subject: [PATCH 1/2] feat(landing): center the tabs header above the tab list and panels The header used to share a column with the tab list, which left a tall empty area above the code panel on desktop. It now sits centered above both columns, which are top-aligned. Amp-Thread-ID: https://ampcode.com/threads/T-53648462-f442-44d1-9e8d-017ac90e9551 Co-authored-by: Amp --- app/components/landing/LandingTabs.vue | 138 +++++++++--------- .../content/2.writing/4.landing-page.md | 2 +- 2 files changed, 70 insertions(+), 70 deletions(-) diff --git a/app/components/landing/LandingTabs.vue b/app/components/landing/LandingTabs.vue index 02de78a..eb34ff7 100644 --- a/app/components/landing/LandingTabs.vue +++ b/app/components/landing/LandingTabs.vue @@ -54,83 +54,82 @@ function onKeydown(event: KeyboardEvent) { container: 'max-w-6xl py-16 sm:py-16 lg:py-16', }" > -
+
+

+ // // +

+

+ +

+

+ +

+
+ +
-
-

- // // -

-

- -

-

- -

-
- -
- -
+ {{ item.description }} +

+
+
-
+
diff --git a/playground/content/2.writing/4.landing-page.md b/playground/content/2.writing/4.landing-page.md index 04f7240..cc66eb4 100644 --- a/playground/content/2.writing/4.landing-page.md +++ b/playground/content/2.writing/4.landing-page.md @@ -113,7 +113,7 @@ Slots: `title`, `description`. ## Tabs -`::landing-tabs` renders a two-column section: a vertical tab list on one side and one panel per tab on the other. Panels fill the dynamic `code-0`, `code-1`, ... slots, in the same order as `items`: +`::landing-tabs` renders a centered header (`headline`, `title`, `description`) above two columns: a vertical tab list on one side and one panel per tab on the other. Panels fill the dynamic `code-0`, `code-1`, ... slots, in the same order as `items`: ````mdc ::landing-tabs From 9526d6be3c2ee9b26804bd52b39f4118b74163e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Chopin?= Date: Wed, 21 Oct 2026 19:27:42 +0200 Subject: [PATCH 2/2] feat(landing): add a header prop to landing-tabs `side` keeps the current layout and stays the default, so existing sites don't change. `center` and `left` put the header above both columns and top-align the tab list and panel. Amp-Thread-ID: https://ampcode.com/threads/T-53648462-f442-44d1-9e8d-017ac90e9551 Co-authored-by: Amp --- app/components/landing/LandingTabs.vue | 93 ++++++++++++------- .../content/2.writing/4.landing-page.md | 3 +- 2 files changed, 64 insertions(+), 32 deletions(-) diff --git a/app/components/landing/LandingTabs.vue b/app/components/landing/LandingTabs.vue index eb34ff7..d5cc38a 100644 --- a/app/components/landing/LandingTabs.vue +++ b/app/components/landing/LandingTabs.vue @@ -5,12 +5,36 @@ interface Tab { description?: string } -const props = defineProps<{ +const props = withDefaults(defineProps<{ items: Tab[] reverse?: boolean | string -}>() + /** + * Where the headline, title and description go: `side` stacks them above the + * tab list in its column; `center` and `left` put them above both columns, + * centered or left-aligned. + */ + header?: 'side' | 'center' | 'left' +}>(), { + reverse: false, + header: 'side', +}) const reversed = computed(() => props.reverse === true || props.reverse === 'true' || props.reverse === '') +const stacked = computed(() => props.header !== 'side') + +/** + * One grid for every layout: the header, tab list and panel are its items. On + * the side layout, the panel spans both rows of the other column, and the + * shorter column sits at the bottom; stacked, the header spans both columns + * and the tab list and panel are top-aligned below it. + */ +const tabColumn = computed(() => reversed.value ? 'lg:col-start-2' : 'lg:col-start-1') +const panelColumn = computed(() => reversed.value ? 'lg:col-start-1' : 'lg:col-start-2') +const headerClass = computed(() => { + if (!stacked.value) return [tabColumn.value, 'lg:self-end'] + return props.header === 'center' ? 'lg:col-span-2 mx-auto max-w-2xl text-center' : 'lg:col-span-2 max-w-2xl' +}) +const panelClass = computed(() => [panelColumn.value, stacked.value ? 'lg:row-start-2 lg:self-start' : 'lg:row-start-1 lg:row-span-2 lg:self-end']) const activeIndex = ref(0) @@ -55,41 +79,45 @@ function onKeydown(event: KeyboardEvent) { }" >
-

- // // -

-

- -

-

- -

-
+

+ // // +

+

+ +

+

+ +

+
-
-
+