Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions content/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ sections:
layout: split
cta_secondary_text: Download open source
cta_secondary_link: /docs/install/
cta_card_label: For humans
cta_agent_label: For agents
cta_agent_prompt: Fetch https://www.pulumi.com/onboard.md and follow its instructions to get me started with Pulumi.
cta_agent_prompt_label: Copy prompt
badge_highlight_text: "Latest release:"
badge_text: "Full support for Terraform and HCL"
badge_link: /releases/terraform-state-backend-modules-hcl/
Expand Down
70 changes: 70 additions & 0 deletions layouts/partials/hero-ctas.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{{/*
Hero CTA row — shared by the split and centered branches of template-hero.

By default this renders the primary/secondary/tertiary buttons in a plain
flex row, exactly as before. Setting cta_card_label groups them into a
bordered card whose label straddles the top border, and setting
cta_agent_prompt adds a second card beside it holding a single
copy-to-clipboard button for coding agents.

Params:
- cta_primary_text / cta_primary_link
- cta_secondary_text / cta_secondary_link
- cta_tertiary_text / cta_tertiary_link
- cta_card_label: Optional. Label for the card wrapping the buttons (e.g. "For humans").
- cta_agent_label: Optional. Label for the agent card. Defaults to "For agents".
- cta_agent_prompt: Optional. Text the agent card's button copies to the clipboard.
- cta_agent_prompt_label: Optional short text shown on that button in place of the
full prompt. The prompt itself is still what gets copied, and still what the
tooltip shows. Defaults to the prompt.
- layout: "split" (buttons go inline at sm) or "centered" (at lg).

Class strings below are written out in full rather than composed from
fragments so Tailwind's source scanner still sees every utility.
*/}}

{{ $split := eq (.layout | default "centered") "split" }}
{{ $rowClass := cond $split "flex flex-col sm:flex-row flex-wrap items-start gap-4 w-full" "flex flex-col lg:flex-row flex-wrap items-center justify-center gap-4 w-full" }}
{{ $btnClass := cond $split "w-full sm:w-auto" "w-full lg:w-auto" }}
{{ $cardLabel := .cta_card_label }}
{{ $agentLabel := .cta_agent_label | default "For agents" }}
{{ $agentPrompt := .cta_agent_prompt }}
{{ $agentPromptLabel := .cta_agent_prompt_label | default $agentPrompt }}

{{ if or .cta_primary_text .cta_secondary_text .cta_tertiary_text }}
<div class="hero-ctas{{ if $agentPrompt }} hero-ctas--split{{ end }}">
<div class="hero-cta-group{{ if $cardLabel }} hero-cta-card{{ end }}">
{{ with $cardLabel }}
<span class="hero-cta-card-label">{{ . }}</span>
{{ end }}
<div class="{{ $rowClass }}">
{{ with .cta_primary_text }}
<a href="{{ $.cta_primary_link }}"{{ partial "external-link-attrs.html" $.cta_primary_link | safeHTMLAttr }} class="btn btn-primary btn-xl {{ $btnClass }}" data-role="cta-get-started">
{{ . }}
{{ partial "icon.html" (dict "name" "arrow-right" "weight" "bold" "class" "size-4") }}
</a>
{{ end }}
{{ with .cta_secondary_text }}
<a href="{{ $.cta_secondary_link }}"{{ partial "external-link-attrs.html" $.cta_secondary_link | safeHTMLAttr }} class="btn btn-outline btn-xl {{ $btnClass }}" data-role="cta-secondary">{{ . }}</a>
{{ end }}
{{ with .cta_tertiary_text }}
<a href="{{ $.cta_tertiary_link }}"{{ partial "external-link-attrs.html" $.cta_tertiary_link | safeHTMLAttr }} class="btn btn-outline btn-xl {{ $btnClass }}">{{ . }}</a>
{{ end }}
</div>
</div>
{{ with $agentPrompt }}
<div class="hero-cta-group hero-cta-card">
<span class="hero-cta-card-label">{{ $agentLabel }}</span>
<pulumi-tooltip class="hero-cta-copy-tooltip">
<button type="button" class="btn btn-outline btn-xl hero-cta-copy" data-copy-text="{{ . }}" data-role="cta-agent-prompt" aria-label="Copy the onboarding prompt for your coding agent">
<span data-copy-idle>{{ partial "icon.html" (dict "name" "copy" "class" "size-5") }}</span>
<span data-copy-done hidden>{{ partial "icon.html" (dict "name" "check" "weight" "bold" "class" "size-5 text-violet-primary") }}</span>
<span class="hero-cta-copy-text" data-copy-idle>{{ $agentPromptLabel }}</span>
<span class="hero-cta-copy-text" data-copy-done hidden>Copied!</span>
</button>
<span slot="content">{{ . }}</span>
</pulumi-tooltip>
</div>
{{ end }}
</div>
{{ end }}
66 changes: 32 additions & 34 deletions layouts/partials/template-partials/template-hero.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@
- cta_tertiary_text: Optional tertiary CTA button text.
- cta_tertiary_link: Optional tertiary CTA button link.

Parameters — CTA cards (see partials/hero-ctas.html):
- cta_card_label: Optional. Groups the buttons above into a bordered card labeled with this text.
- cta_agent_prompt: Optional. Adds a second card holding a button that copies this text to the clipboard.
- cta_agent_prompt_label: Optional short text shown on that button instead of the full prompt.
- cta_agent_label: Optional label for that second card. Defaults to "For agents".

Right-slot precedence: code_snippets > video_youtube_id > image.
*/}}

Expand All @@ -62,6 +68,10 @@
{{ $ctaSecondaryLink := .cta_secondary_link | default site.Params.cta.secondary.href }}
{{ $ctaTertiaryText := .cta_tertiary_text }}
{{ $ctaTertiaryLink := .cta_tertiary_link }}
{{ $ctaCardLabel := .cta_card_label }}
{{ $ctaAgentLabel := .cta_agent_label }}
{{ $ctaAgentPrompt := .cta_agent_prompt }}
{{ $ctaAgentPromptLabel := .cta_agent_prompt_label }}
{{ $videoYouTubeId := .video_youtube_id }}
{{ $videoTitle := .video_title | default "Watch video" }}
{{ $image := .image }}
Expand Down Expand Up @@ -102,7 +112,13 @@
{{ if eq $layout "split" }}
<section class="template-hero"{{ if $anchor }} id="{{ $anchor }}"{{ end }}>
<div class="flex flex-col xl:flex-row items-center justify-between gap-12">
<div class="xl:w-1/2 flex flex-col items-start">
{{/* w-full below xl: without it the column is content-sized, so a wide
nowrap child (e.g. the agent prompt) widens it past the viewport.
min-w-0 at xl: the columns' percentage bases plus the gap overflow the
line, so both shrink — and without this the column's min-content size
props it back up, making its width depend on its text. That reflows
the whole row whenever child text changes. */}}
<div class="w-full min-w-0 xl:w-[55%] flex flex-col items-start">
{{ if or $badgeText $badgeHighlightText }}
<a href="{{ $badgeLink }}" class="font-overline flex-wrap inline-flex items-center gap-1 border border-violet-300 rounded-full py-3 px-5 no-underline hover:border-violet-500 transition-colors">
<span class="text-violet-primary whitespace-nowrap">{{ $badgeHighlightText }}</span>
Expand Down Expand Up @@ -146,25 +162,16 @@ <h1 class="product-hero-heading text-left">
{{ if $description }}
<p class="product-hero-description mb-10 text-left">{{ $description | markdownify }}</p>
{{ end }}
{{ if or $ctaPrimaryText $ctaSecondaryText $ctaTertiaryText }}
<div class="flex flex-col sm:flex-row flex-wrap items-start gap-4 w-full">
{{ if $ctaPrimaryText }}
<a href="{{ $ctaPrimaryLink }}"{{ partial "external-link-attrs.html" $ctaPrimaryLink | safeHTMLAttr }} class="btn btn-primary btn-xl w-full sm:w-auto" data-role="cta-get-started">
{{ $ctaPrimaryText }}
{{ partial "icon.html" (dict "name" "arrow-right" "weight" "bold" "class" "size-4") }}
</a>
{{ end }}
{{ if $ctaSecondaryText }}
<a href="{{ $ctaSecondaryLink }}"{{ partial "external-link-attrs.html" $ctaSecondaryLink | safeHTMLAttr }} class="btn btn-outline btn-xl w-full sm:w-auto" data-role="cta-secondary">{{ $ctaSecondaryText }}</a>
{{ end }}
{{ if $ctaTertiaryText }}
<a href="{{ $ctaTertiaryLink }}"{{ partial "external-link-attrs.html" $ctaTertiaryLink | safeHTMLAttr }} class="btn btn-outline btn-xl w-full sm:w-auto">{{ $ctaTertiaryText }}</a>
{{ end }}
</div>
{{ end }}
{{ partial "hero-ctas.html" (dict
"layout" "split"
"cta_primary_text" $ctaPrimaryText "cta_primary_link" $ctaPrimaryLink
"cta_secondary_text" $ctaSecondaryText "cta_secondary_link" $ctaSecondaryLink
"cta_tertiary_text" $ctaTertiaryText "cta_tertiary_link" $ctaTertiaryLink
"cta_card_label" $ctaCardLabel "cta_agent_label" $ctaAgentLabel "cta_agent_prompt" $ctaAgentPrompt
"cta_agent_prompt_label" $ctaAgentPromptLabel) }}
</div>
{{ if $codeSnippets }}
<div class="xl:w-1/2 w-full flex justify-end">
<div class="xl:w-[40%] w-full flex justify-end">
{{ if or $codeAspectRatio $overlayImage }}
<div class="hero-code-visual"{{ with $visualStyle }} style="{{ . | safeCSS }}"{{ end }}>
<div class="hero-code-block"{{ with $blockOffsetStyle }} style="{{ . | safeCSS }}"{{ end }}>
Expand Down Expand Up @@ -232,22 +239,13 @@ <h1 class="product-hero-heading">
{{ if $description }}
<p class="product-hero-description mb-10">{{ $description | markdownify }}</p>
{{ end }}
{{ if or $ctaPrimaryText $ctaSecondaryText $ctaTertiaryText }}
<div class="flex flex-col lg:flex-row flex-wrap items-center justify-center gap-4 w-full">
{{ if $ctaPrimaryText }}
<a href="{{ $ctaPrimaryLink }}"{{ partial "external-link-attrs.html" $ctaPrimaryLink | safeHTMLAttr }} class="btn btn-primary btn-xl w-full lg:w-auto" data-role="cta-get-started">
{{ $ctaPrimaryText }}
{{ partial "icon.html" (dict "name" "arrow-right" "weight" "bold" "class" "size-4") }}
</a>
{{ end }}
{{ if $ctaSecondaryText }}
<a href="{{ $ctaSecondaryLink }}"{{ partial "external-link-attrs.html" $ctaSecondaryLink | safeHTMLAttr }} class="btn btn-outline btn-xl w-full lg:w-auto" data-role="cta-secondary">{{ $ctaSecondaryText }}</a>
{{ end }}
{{ if $ctaTertiaryText }}
<a href="{{ $ctaTertiaryLink }}"{{ partial "external-link-attrs.html" $ctaTertiaryLink | safeHTMLAttr }} class="btn btn-outline btn-xl w-full lg:w-auto">{{ $ctaTertiaryText }}</a>
{{ end }}
</div>
{{ end }}
{{ partial "hero-ctas.html" (dict
"layout" "centered"
"cta_primary_text" $ctaPrimaryText "cta_primary_link" $ctaPrimaryLink
"cta_secondary_text" $ctaSecondaryText "cta_secondary_link" $ctaSecondaryLink
"cta_tertiary_text" $ctaTertiaryText "cta_tertiary_link" $ctaTertiaryLink
"cta_card_label" $ctaCardLabel "cta_agent_label" $ctaAgentLabel "cta_agent_prompt" $ctaAgentPrompt
"cta_agent_prompt_label" $ctaAgentPromptLabel) }}
</div>
{{ if $codeSnippets }}
<div class="product-hero-visual flex justify-center w-full">
Expand Down
82 changes: 81 additions & 1 deletion theme/src/scss/_templates.scss
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@
}

.product-hero-title-secondary {
@apply block text-black;
@apply block text-black max-w-lg;
}

.product-hero-title {
Expand All @@ -138,6 +138,86 @@
.product-hero-visual {
@apply w-full;
}

// CTA row (partials/hero-ctas.html). Plain full-width wrapper by default;
// --split sets the "for humans" and "for agents" cards side by side above
// mobile. The humans card keeps its natural width so its buttons never wrap;
// the agents card takes whatever is left and truncates its prompt into it.
.hero-ctas {
@apply w-full;

&--split {
@apply flex flex-col gap-4;

// sm → md: the cards have stacked but the buttons are already in a
// row, so the humans card spans the full width while its buttons hug
// their text. Stretch them to fill it. Below sm they are w-full
// already; from md the card is shrink-0 and sized to them.
@media (min-width: 640px) and (max-width: 767.98px) {
> .hero-cta-group:first-child .btn {
@apply flex-1;
}
}

@include screen-md {
@apply flex-row items-stretch;

> .hero-cta-group:first-child {
@apply shrink-0;
}

// The agents card hugs its button too, rather than growing into
// the leftover space — the button holds a short label now.
> .hero-cta-group:last-child {
@apply shrink-0;
}
}
}
}

// Bordered card with its label straddling the top border. The label's own
// white background is what breaks the border line on either side of it, so
// this only reads correctly on a white section background.
.hero-cta-card {
@apply relative rounded-lg border border-violet-300 p-3;
}

.hero-cta-card-label {
@apply font-overline-sm absolute -top-2 left-4 px-2 bg-white text-violet-primary;
}

// The agent prompt button: copy icon plus a short label. The prompt itself
// lives in the tooltip and the clipboard, not here.
//
// The min-width holds the button steady when the label swaps to the copied
// state, which is otherwise narrower; sized just past the wider of the two
// (10.25rem) so neither reflows. Revisit it if either label changes.
.hero-cta-copy {
@apply w-full min-w-[10.5rem];
}

// pulumi-tooltip is inline by default; the trigger here is a full-width
// button, so stretch both to the card. The bubble holds the whole prompt, so
// it gets a fixed width wider than both the button and the component's own
// w-48 default, and wraps rather than truncating.
.hero-cta-copy-tooltip {
@apply block w-full;

.tooltip-target {
@apply block w-full;
}

.tooltip-content {
@apply w-72 text-left break-words font-mono;
}
}

// Safety net for a long cta_agent_prompt_label: min-w-0 lets the span shrink
// below its max-content width so the ellipsis renders rather than the text
// being flatly clipped by the button.
.hero-cta-copy-text {
@apply truncate min-w-0;
}
}

.template-logo-banner {
Expand Down
Loading