From 1ea71c09a617dea1ca4650c572515d31b6f685ac Mon Sep 17 00:00:00 2001 From: Shaun Andrews Date: Mon, 31 Aug 2026 20:14:11 -0400 Subject: [PATCH 1/2] Refine Blueprint discovery and creation details --- .../blueprint-gallery/index.test.tsx | 47 +++- .../components/blueprint-gallery/index.tsx | 162 +++++++------ .../blueprint-gallery/style.module.css | 168 +++++++------ .../blueprint-gallery/upload-tile.tsx | 54 ----- .../src/components/create-site-form/index.tsx | 6 +- .../create-site-form/style.module.css | 87 +++---- .../use-wordpress-org-package-name.test.ts | 37 +++ .../queries/use-wordpress-org-package-name.ts | 48 ++++ apps/ui/src/lib/blueprint-selection.ts | 2 + .../route-onboarding-create/index.test.tsx | 74 +++++- .../router/route-onboarding-create/index.tsx | 222 +++++++++++++++++- .../route-onboarding-create/style.module.css | 151 ++++++++++++ .../route-onboarding-home/style.module.css | 40 ++-- 13 files changed, 819 insertions(+), 279 deletions(-) delete mode 100644 apps/ui/src/components/blueprint-gallery/upload-tile.tsx create mode 100644 apps/ui/src/data/queries/use-wordpress-org-package-name.test.ts create mode 100644 apps/ui/src/data/queries/use-wordpress-org-package-name.ts diff --git a/apps/ui/src/components/blueprint-gallery/index.test.tsx b/apps/ui/src/components/blueprint-gallery/index.test.tsx index d07aab6c99..662becb792 100644 --- a/apps/ui/src/components/blueprint-gallery/index.test.tsx +++ b/apps/ui/src/components/blueprint-gallery/index.test.tsx @@ -32,6 +32,7 @@ function apiBlueprint( slug: string, title: string, excerpt = '' ): Blueprint { } const QUICK_START = apiBlueprint( 'quick-start', 'Quick Start' ); +const WOO_COMMERCE = apiBlueprint( 'woo-shop', 'Commerce' ); const COOKBOOK = apiBlueprint( 'cookbook', 'Cookbook', 'Recipes and menus' ); describe( 'BlueprintGallery', () => { @@ -55,11 +56,30 @@ describe( 'BlueprintGallery', () => { fireEvent.click( screen.getByRole( 'button', { name: /WordPress\.com/ } ) ); expect( onSelect ).toHaveBeenCalledWith( - expect.objectContaining( { title: 'WordPress.com', excerpt: expect.any( String ) } ) + expect.objectContaining( { + slug: 'quick-start', + title: 'WordPress.com', + excerpt: expect.any( String ), + } ) ); } ); - it( 'filters Explore by excerpt while leaving Featured in place', () => { + it( 'filters the complete gallery by excerpt', () => { + mocks.blueprints = { + data: [ + WOO_COMMERCE, + QUICK_START, + ...Array.from( { length: 21 }, ( _, index ) => + apiBlueprint( + `explore-${ index }`, + index === 0 ? 'Cookbook' : `Blueprint ${ index }`, + index === 0 ? 'Recipes and menus' : 'A sample Blueprint' + ) + ), + ], + isLoading: false, + isError: false, + }; render( ); const search = screen.getByRole( 'searchbox', { name: 'Search Blueprints' } ); @@ -68,7 +88,28 @@ describe( 'BlueprintGallery', () => { fireEvent.change( search, { target: { value: 'nothing here' } } ); expect( screen.getByText( 'No Blueprints found.' ) ).toBeInTheDocument(); - expect( screen.getByRole( 'button', { name: /WordPress\.com/ } ) ).toBeInTheDocument(); + expect( screen.queryByRole( 'button', { name: /WooCommerce/ } ) ).not.toBeInTheDocument(); + } ); + + it( 'hides Search for a small Blueprint collection', () => { + render( ); + + expect( + screen.queryByRole( 'searchbox', { name: 'Search Blueprints' } ) + ).not.toBeInTheDocument(); + } ); + + it( 'pins development, WordPress.com, and WooCommerce ahead of the API order', () => { + mocks.blueprints = { + data: [ COOKBOOK, WOO_COMMERCE, apiBlueprint( 'development', 'Development' ), QUICK_START ], + isLoading: false, + isError: false, + }; + render( ); + + expect( + screen.getAllByRole( 'heading', { level: 3 } ).map( ( heading ) => heading.textContent ) + ).toEqual( [ 'Theme & plugin development', 'WordPress.com', 'WooCommerce', 'Cookbook' ] ); } ); it( 'hands an uploaded Blueprint upward like a curated pick', async () => { diff --git a/apps/ui/src/components/blueprint-gallery/index.tsx b/apps/ui/src/components/blueprint-gallery/index.tsx index 18e0e58113..3e1358639d 100644 --- a/apps/ui/src/components/blueprint-gallery/index.tsx +++ b/apps/ui/src/components/blueprint-gallery/index.tsx @@ -1,21 +1,24 @@ import { __ } from '@wordpress/i18n'; +import { upload } from '@wordpress/icons'; +import { Button, Icon } from '@wordpress/ui'; import { useCallback, useMemo, useRef, useState } from 'react'; import { useConnector } from '@/data/core'; import { useBlueprints } from '@/data/queries/use-blueprints'; import { useOffline } from '@/hooks/use-offline'; import { BLUEPRINT_FILE_ACCEPT, loadBlueprintFile } from '@/lib/load-blueprint-file'; import styles from './style.module.css'; -import { UploadTile } from './upload-tile'; import type { SelectedBlueprint } from '@/lib/blueprint-selection'; import type { Blueprint } from '@studio/common/lib/studio-blueprints-api'; import type { BlueprintV1Declaration } from '@wp-playground/blueprints'; -const FEATURED_BLUEPRINT_SLUGS = new Set( [ 'woo-shop', 'development', 'quick-start' ] ); +const BLUEPRINT_ARTWORK_SLUGS = new Set( [ 'woo-shop', 'development', 'quick-start' ] ); +const SEARCH_THRESHOLD = 20; const BLUEPRINT_DISPLAY_NAMES: Record< string, string > = { 'Quick Start': 'WordPress.com', - Development: 'WordPress Dev', + Development: 'Theme & plugin development', Commerce: 'WooCommerce', + 'Stylish Press': 'WooCommerce starter store', }; function getBlueprintExcerptOverrides(): Record< string, string > { @@ -26,20 +29,28 @@ function getBlueprintExcerptOverrides(): Record< string, string > { Commerce: __( 'Create your next online store with WooCommerce and its companion plugins pre-installed.' ), - Development: __( 'A streamlined environment for building and testing themes or plugins.' ), + Development: __( + 'Query Monitor, Plugin Check, Theme Check, and Create Block Theme pre-installed.' + ), + 'Stylish Press': __( + 'A ready-made WooCommerce store with a custom theme, sample content, and products.' + ), }; } -const BLUEPRINT_ORDER: Record< string, number > = { - 'Quick Start': 1, - Commerce: 2, - Development: 3, -}; +const PINNED_BLUEPRINT_SLUGS = [ 'development', 'quick-start', 'woo-shop' ]; +const PINNED_BLUEPRINT_ORDER = new Map( + PINNED_BLUEPRINT_SLUGS.map( ( slug, index ) => [ slug, index ] ) +); function renameBlueprintsForDisplay( blueprints: Blueprint[] ): Blueprint[] { const excerptOverrides = getBlueprintExcerptOverrides(); return [ ...blueprints ] - .sort( ( a, b ) => ( BLUEPRINT_ORDER[ a.title ] ?? 99 ) - ( BLUEPRINT_ORDER[ b.title ] ?? 99 ) ) + .sort( + ( a, b ) => + ( PINNED_BLUEPRINT_ORDER.get( a.slug ) ?? Number.MAX_SAFE_INTEGER ) - + ( PINNED_BLUEPRINT_ORDER.get( b.slug ) ?? Number.MAX_SAFE_INTEGER ) + ) .map( ( item ) => ( { ...item, excerpt: excerptOverrides[ item.title ] || item.excerpt, @@ -49,8 +60,10 @@ function renameBlueprintsForDisplay( blueprints: Blueprint[] ): Blueprint[] { function apiToSelectedBlueprint( bp: Blueprint ): SelectedBlueprint { return { + slug: bp.slug, title: bp.title, excerpt: bp.excerpt, + image: bp.image, blueprint: bp.blueprint as BlueprintV1Declaration, file: { name: bp.title, size: 0 }, filePath: bp.filePath, @@ -58,11 +71,28 @@ function apiToSelectedBlueprint( bp: Blueprint ): SelectedBlueprint { }; } -function BlueprintCard( { blueprint, onClick }: { blueprint: Blueprint; onClick: () => void } ) { +function BlueprintCard( { + blueprint, + onClick, + cropAdminBar = false, +}: { + blueprint: Blueprint; + onClick: () => void; + cropAdminBar?: boolean; +} ) { return ( + + { isReading ? __( 'Reading Blueprint…' ) : __( 'Upload a Blueprint' ) } + + { error && ( + + { error } + + ) } ); } @@ -163,26 +186,17 @@ export function BlueprintGallery( { onSelect }: BlueprintGalleryProps ) { const isOffline = useOffline(); const { data: blueprints, isLoading, isError } = useBlueprints(); const [ searchQuery, setSearchQuery ] = useState( '' ); - - const featured = useMemo( - () => - renameBlueprintsForDisplay( - ( blueprints ?? [] ).filter( ( bp ) => FEATURED_BLUEPRINT_SLUGS.has( bp.slug ) ) - ), + const displayBlueprints = useMemo( + () => renameBlueprintsForDisplay( blueprints ?? [] ), [ blueprints ] ); - const explore = useMemo( - () => ( blueprints ?? [] ).filter( ( bp ) => ! FEATURED_BLUEPRINT_SLUGS.has( bp.slug ) ), - [ blueprints ] - ); - - const filteredExplore = useMemo( () => { + const filteredBlueprints = useMemo( () => { const query = searchQuery.toLowerCase().trim(); if ( ! query ) { - return explore; + return displayBlueprints; } - return explore.filter( ( bp ) => { + return displayBlueprints.filter( ( bp ) => { const titleMatch = bp.title.toLowerCase().includes( query ); const excerptMatch = bp.excerpt.toLowerCase().includes( query ); const meta = ( bp.blueprint as Record< string, unknown > )?.meta as @@ -192,21 +206,37 @@ export function BlueprintGallery( { onSelect }: BlueprintGalleryProps ) { const categoryMatch = categoryList.some( ( cat ) => cat.toLowerCase().includes( query ) ); return titleMatch || excerptMatch || categoryMatch; } ); - }, [ explore, searchQuery ] ); + }, [ displayBlueprints, searchQuery ] ); const renderCard = ( bp: Blueprint ) => ( onSelect( apiToSelectedBlueprint( bp ) ) } /> ); return (
- +
+ +
+ { displayBlueprints.length > SEARCH_THRESHOLD && ( + setSearchQuery( event.target.value ) } + /> + ) } + +
+
{ isLoading ? (

{ __( 'Loading Blueprints…' ) }

@@ -214,34 +244,10 @@ export function BlueprintGallery( { onSelect }: BlueprintGalleryProps ) {

{ __( 'Blueprints could not be loaded. Check your internet connection and try again.' ) }

+ ) : filteredBlueprints.length === 0 ? ( +

{ __( 'No Blueprints found.' ) }

) : ( - <> -
- { featured.map( renderCard ) } - -
- - { explore.length > 0 && ( - <> -
-

{ __( 'Explore more Blueprints' ) }

- setSearchQuery( event.target.value ) } - /> -
- { filteredExplore.length === 0 ? ( -

{ __( 'No Blueprints found.' ) }

- ) : ( -
{ filteredExplore.map( renderCard ) }
- ) } - - ) } - +
{ filteredBlueprints.map( renderCard ) }
) }
); diff --git a/apps/ui/src/components/blueprint-gallery/style.module.css b/apps/ui/src/components/blueprint-gallery/style.module.css index b682776fdc..34164fb87a 100644 --- a/apps/ui/src/components/blueprint-gallery/style.module.css +++ b/apps/ui/src/components/blueprint-gallery/style.module.css @@ -1,21 +1,33 @@ .root { - margin-top: 40px; - padding-top: 32px; - border-top: var(--wpds-border-width-xs) solid var(--wpds-color-stroke-surface-neutral); + margin-top: 112px; text-align: center; } .heading { - margin: 0 0 20px; - font-size: var(--wpds-typography-font-size-lg); + margin: 0; + font-size: var( --wpds-typography-font-size-lg ); font-weight: 600; - color: var(--wpds-color-fg-content-neutral); + color: var( --wpds-color-fg-content-neutral ); +} + +.sectionHeader { + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + margin-bottom: 20px; } +.sectionActions { + display: flex; + align-items: center; + gap: 8px; +} .grid { display: grid; - grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + grid-template-columns: repeat( auto-fill, minmax( 220px, 1fr ) ); gap: 16px; text-align: left; } @@ -24,9 +36,10 @@ display: flex; flex-direction: column; overflow: hidden; - border: var(--wpds-border-width-xs) solid var(--wpds-color-stroke-surface-neutral); + border: var( --wpds-border-width-xs ) solid var( --wpds-color-stroke-surface-neutral ); border-radius: 10px; - background: color-mix(in srgb, var(--wpds-color-bg-surface-neutral-strong) 50%, transparent); + background: color-mix( in srgb, var( --wpds-color-bg-surface-neutral-strong ) 50%, transparent ); + backdrop-filter: blur( 12px ); cursor: pointer; transition: border-color 0.15s ease; text-align: left; @@ -37,22 +50,44 @@ } .card:hover { - border-color: var(--wpds-color-stroke-interactive-brand); - box-shadow: 0 0 0 1px var(--wpds-color-stroke-interactive-brand); + border-color: var( --wpds-color-stroke-interactive-brand ); + box-shadow: 0 0 0 1px var( --wpds-color-stroke-interactive-brand ); } .card:focus-visible { - outline: 2px solid var(--wpds-color-stroke-focus-brand); + outline: 2px solid var( --wpds-color-stroke-focus-brand ); outline-offset: 2px; - border-color: var(--wpds-color-stroke-interactive-brand); + border-color: var( --wpds-color-stroke-interactive-brand ); } -.cardImage { +.cardImageViewport { + position: relative; display: block; width: 100%; aspect-ratio: 16 / 9; + overflow: hidden; + background: var( --wpds-color-bg-surface-neutral ); +} + +.cardImage { + display: block; + width: 100%; + height: 100%; object-fit: cover; - background: var(--wpds-color-bg-surface-neutral); + transition: transform 8s cubic-bezier( 0.2, 0.65, 0.3, 1 ); +} + +.card:hover .cardImage, +.card:focus-visible .cardImage { + transform: scale( 1.05 ); +} + +.cardImageWithoutAdminBar { + position: absolute; + bottom: 0; + left: 0; + height: 103%; + object-position: bottom; } .cardImageFallback { @@ -61,9 +96,9 @@ justify-content: center; width: 100%; aspect-ratio: 16 / 9; - background: var(--wpds-color-bg-surface-neutral); - color: var(--wpds-color-fg-content-neutral-weak); - font-size: var(--wpds-typography-font-size-sm); + background: var( --wpds-color-bg-surface-neutral ); + color: var( --wpds-color-fg-content-neutral-weak ); + font-size: var( --wpds-typography-font-size-sm ); } .cardBody { @@ -75,97 +110,84 @@ .cardTitle { margin: 0; - font-size: var(--wpds-typography-font-size-md); + font-size: var( --wpds-typography-font-size-md ); font-weight: 500; - color: var(--wpds-color-fg-content-neutral); + color: var( --wpds-color-fg-content-neutral ); transition: color 0.15s ease; } .card:hover .cardTitle { - color: var(--wpds-color-fg-interactive-brand); + color: var( --wpds-color-fg-interactive-brand ); } .cardExcerpt { margin: 0; - color: var(--wpds-color-fg-content-neutral-weak); - font-size: var(--wpds-typography-font-size-sm); + color: var( --wpds-color-fg-content-neutral-weak ); + font-size: var( --wpds-typography-font-size-sm ); line-height: 1.4; display: -webkit-box; - -webkit-line-clamp: 2; + -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; } -.exploreHeader { - display: flex; - align-items: center; - justify-content: space-between; - gap: 16px; - margin-top: 32px; - margin-bottom: 16px; -} - -.exploreTitle { - margin: 0; - font-size: var(--wpds-typography-font-size-md); - font-weight: 600; - color: var(--wpds-color-fg-content-neutral); -} - .searchInput { max-width: 200px; padding: 6px 10px; - border: var(--wpds-border-width-xs) solid var(--wpds-color-stroke-surface-neutral); + border: var( --wpds-border-width-xs ) solid var( --wpds-color-stroke-surface-neutral ); border-radius: 4px; - background: var(--wpds-color-bg-surface-neutral-strong); - color: var(--wpds-color-fg-content-neutral); + background: var( --wpds-color-bg-surface-neutral-strong ); + color: var( --wpds-color-fg-content-neutral ); font: inherit; - font-size: var(--wpds-typography-font-size-sm); + font-size: var( --wpds-typography-font-size-sm ); } .searchInput:focus-visible { - outline: 2px solid var(--wpds-color-stroke-focus-brand); + outline: 2px solid var( --wpds-color-stroke-focus-brand ); outline-offset: -1px; - border-color: var(--wpds-color-stroke-interactive-brand); + border-color: var( --wpds-color-stroke-interactive-brand ); } .notice { margin: 0; padding: 24px; - color: var(--wpds-color-fg-content-neutral-weak); - font-size: var(--wpds-typography-font-size-sm); + color: var( --wpds-color-fg-content-neutral-weak ); + font-size: var( --wpds-typography-font-size-sm ); } .hiddenInput { display: none; } -/* Matches .cardImage so the upload tile crops and sits exactly like the - curated artwork beside it. */ -.uploadTile { - display: block; - width: 100%; - aspect-ratio: 16 / 9; - overflow: hidden; -} - -/* The artwork is 1160x808, so at full width it is taller than this 16:9 frame - and overflows. Block layout would clip all of that off the bottom, but the - curated PNGs use object-fit: cover, which crops top and bottom equally — so - pull the svg up by half the overflow to match. Percentage margins resolve - against the containing block's width, which is what the overflow scales with: - w * (808/1160 - 9/16) / 2 = w * 6.7026%. */ -.uploadTile svg { - margin-top: -6.7026%; -} - -.uploadCardDragging, -.uploadCardDragging:hover { - border-color: var(--wpds-color-stroke-interactive-brand); - box-shadow: 0 0 0 2px var(--wpds-color-stroke-interactive-brand); +.uploadButtonDragging { + outline: 2px solid var( --wpds-color-stroke-interactive-brand ); + outline-offset: 2px; } .uploadError { - color: var(--wpds-color-fg-content-error); - font-size: var(--wpds-typography-font-size-sm); + position: absolute; + top: 100%; + right: 0; + max-width: 320px; + margin-top: 4px; + color: var( --wpds-color-fg-content-error ); + font-size: var( --wpds-typography-font-size-sm ); + text-align: right; +} + +@media ( max-width: 520px ) { + .sectionHeader { + align-items: flex-start; + flex-direction: column; + } + + .sectionActions { + flex-wrap: wrap; + } +} + +@media ( prefers-reduced-motion: reduce ) { + .cardImage { + transition: none; + } } diff --git a/apps/ui/src/components/blueprint-gallery/upload-tile.tsx b/apps/ui/src/components/blueprint-gallery/upload-tile.tsx deleted file mode 100644 index 246417ab3a..0000000000 --- a/apps/ui/src/components/blueprint-gallery/upload-tile.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import styles from './style.module.css'; - -/** - * Stands in for the artwork the curated Blueprints carry, so the upload choice - * reads as a peer of theirs rather than a bare icon. - * - * Deliberately fixed-color like those tiles (they are flat PNGs that don't - * follow the theme), and it reuses their construction-guide motif — centre - * cross, outer square, inner square — on blueprint navy. The outer square is - * dashed here to mark this as the empty slot you fill. - * - * The viewBox and `slice` match the 1160x808 artwork so the crop lines up. - */ -export function UploadTile() { - return ( -
- { /* data-keep-size opts out of the compact-density rule in index.css - that shrinks every svg to 16px; the frame above sets the size. */ } - -
- ); -} diff --git a/apps/ui/src/components/create-site-form/index.tsx b/apps/ui/src/components/create-site-form/index.tsx index 57e49087cb..36cc8daa82 100644 --- a/apps/ui/src/components/create-site-form/index.tsx +++ b/apps/ui/src/components/create-site-form/index.tsx @@ -35,7 +35,7 @@ import type { FormField, FormValidity, } from '@wordpress/dataviews'; -import type { FormEvent } from 'react'; +import type { FormEvent, ReactNode } from 'react'; export interface CreateSiteFormValues { name: string; @@ -66,6 +66,7 @@ interface CreateSiteFormProps { submitLabel?: string; cancelLabel?: string; loadingAnnouncement?: string; + panelFooter?: ReactNode; } interface FormData { @@ -402,6 +403,7 @@ export function CreateSiteForm( { submitLabel, cancelLabel, loadingAnnouncement, + panelFooter, }: CreateSiteFormProps ) { const formRef = useRef< HTMLFormElement >( null ); const initialSuggestedFields = getSuggestedFields( initialValues ?? {} ); @@ -713,6 +715,8 @@ export function CreateSiteForm( { ) } ) } + + { panelFooter &&
{ panelFooter }
} { actions } diff --git a/apps/ui/src/components/create-site-form/style.module.css b/apps/ui/src/components/create-site-form/style.module.css index b8584d7854..d17b339ef0 100644 --- a/apps/ui/src/components/create-site-form/style.module.css +++ b/apps/ui/src/components/create-site-form/style.module.css @@ -7,14 +7,14 @@ flex-direction: column; gap: 24px; padding: 32px; - border: var(--wpds-border-width-xs) solid var(--wpds-color-stroke-surface-neutral); + border: var( --wpds-border-width-xs ) solid var( --wpds-color-stroke-surface-neutral ); border-radius: 12px; background: color-mix( in srgb, - var(--wpds-color-bg-surface-neutral-strong) var(--surface-alpha, 50%), + var( --wpds-color-bg-surface-neutral-strong ) var( --surface-alpha, 50% ), transparent ); - backdrop-filter: blur(12px); + backdrop-filter: blur( 12px ); } /* The path trigger is a + +
+ { selected.image && ( +
+ +
+ ) } +
+

+ { selected.title } +

+ { selected.excerpt && ( +

{ selected.excerpt }

+ ) } +
+
+ { ( packages.plugins.length > 0 || packages.themes.length > 0 ) && ( +
+ { packages.plugins.length > 0 && ( +
+

+ { sprintf( + _n( '%d plugin', '%d plugins', packages.plugins.length ), + packages.plugins.length + ) } +

+
    + { packages.plugins.map( ( plugin ) => ( +
  • + +
  • + ) ) } +
+
+ ) } + { packages.themes.length > 0 && ( +
+

+ { sprintf( + _n( '%d theme', '%d themes', packages.themes.length ), + packages.themes.length + ) } +

+
    + { packages.themes.map( ( theme ) => ( +
  • + +
  • + ) ) } +
+
+ ) } +
+ ) } + + ); +} + export function CreateSitePage() { const connector = useConnector(); const navigate = useNavigate(); @@ -167,15 +358,28 @@ export function CreateSitePage() { isSubmitDisabled={ ! isBlueprintValid } submitError={ submitError } submitLabel={ selectedBlueprint ? __( 'Create site from Blueprint' ) : undefined } + panelFooter={ + selectedBlueprint ? ( + { + setIsBlueprintValid( true ); + replaceBlueprint( null ); + } } + /> + ) : undefined + } /> -
- replaceBlueprint( null ) } - onValidityChange={ setIsBlueprintValid } - /> -
+ { ! selectedBlueprint && ( +
+ replaceBlueprint( null ) } + onValidityChange={ setIsBlueprintValid } + /> +
+ ) } ); } diff --git a/apps/ui/src/ui-classic/router/route-onboarding-create/style.module.css b/apps/ui/src/ui-classic/router/route-onboarding-create/style.module.css index c61f00950f..ea734a4112 100644 --- a/apps/ui/src/ui-classic/router/route-onboarding-create/style.module.css +++ b/apps/ui/src/ui-classic/router/route-onboarding-create/style.module.css @@ -1,3 +1,154 @@ .blueprint { margin-top: 32px; } + +.summary { + width: 100%; + margin: 0; + text-align: left; +} + +.summaryHeader { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + margin-bottom: 12px; +} + +.summaryHeader h2 { + margin: 0; + color: var( --wpds-color-fg-content-neutral ); + font-size: var( --wpds-typography-font-size-md ); + font-weight: 600; +} + +.summaryRemove { + padding: 0; + border: 0; + background: transparent; + color: var( --wpds-color-fg-interactive-brand ); + font: inherit; + font-size: var( --wpds-typography-font-size-sm ); + cursor: pointer; +} + +.summaryRemove:hover { + text-decoration: underline; +} + +.summaryRemove:focus-visible { + border-radius: 2px; + outline: 2px solid var( --wpds-color-stroke-focus-brand ); + outline-offset: 2px; +} + +.summaryOverview { + display: grid; + grid-template-columns: 112px minmax( 0, 1fr ); + align-items: start; + gap: 16px; +} + +.summaryOverviewWithoutImage { + grid-template-columns: 1fr; +} + +.summaryImageViewport { + position: relative; + width: 112px; + aspect-ratio: 16 / 10; + overflow: hidden; + border-radius: 6px; + background: var( --wpds-color-bg-surface-neutral ); +} + +.summaryImage { + display: block; + width: 100%; + height: 100%; + object-fit: cover; +} + +.summaryImageWithoutAdminBar { + position: absolute; + bottom: 0; + left: 0; + height: 103%; + object-position: bottom; +} + +.summaryContent { + min-width: 0; +} + +.summaryTitle { + margin: 0; + color: var( --wpds-color-fg-content-neutral ); + font-size: var( --wpds-typography-font-size-md ); + font-weight: 600; +} + +.summaryExcerpt { + margin: 4px 0 0; + color: var( --wpds-color-fg-content-neutral-weak ); + font-size: var( --wpds-typography-font-size-sm ); + line-height: 1.4; +} + +.summaryPackages { + display: grid; + grid-template-columns: repeat( 2, minmax( 0, 1fr ) ); + gap: 24px; + margin-top: 24px; +} + +.summaryPackageGroup h3 { + margin: 0 0 6px; + color: var( --wpds-color-fg-content-neutral ); + font-size: var( --wpds-typography-font-size-sm ); + font-weight: 600; +} + +.summaryPackageGroup:only-child { + grid-column: 1 / -1; +} + +.summaryPackageGroup ul { + margin: 0; + padding-left: 18px; +} + +.summaryPackageGroup li { + margin-top: 3px; + color: var( --wpds-color-fg-content-neutral-weak ); + font-size: var( --wpds-typography-font-size-sm ); +} + +.summaryPackageGroup a { + color: var( --wpds-color-fg-interactive-brand ); + overflow-wrap: anywhere; + text-decoration: none; +} + +.summaryPackageGroup a:hover { + text-decoration: underline; +} + +.summaryPackageGroup a:focus-visible { + border-radius: 2px; + outline: 2px solid var( --wpds-color-stroke-focus-brand ); + outline-offset: 2px; +} + +@media ( max-width: 520px ) { + .summaryOverview, + .summaryPackages { + grid-template-columns: 1fr; + } + + .summaryImageViewport { + width: 100%; + aspect-ratio: 16 / 9; + } +} diff --git a/apps/ui/src/ui-classic/router/route-onboarding-home/style.module.css b/apps/ui/src/ui-classic/router/route-onboarding-home/style.module.css index b4434afaa3..53d9ccd96c 100644 --- a/apps/ui/src/ui-classic/router/route-onboarding-home/style.module.css +++ b/apps/ui/src/ui-classic/router/route-onboarding-home/style.module.css @@ -1,14 +1,14 @@ .page { width: 100%; text-align: center; + padding-top: 48px; padding-bottom: 72px; } .cards { display: grid; - grid-template-columns: repeat(auto-fit, minmax(240px, 300px)); + grid-template-columns: repeat( 3, minmax( 0, 1fr ) ); gap: 20px; - justify-content: center; } .card { @@ -17,14 +17,14 @@ align-items: center; gap: 16px; padding: 24px; - border: var(--wpds-border-width-xs) solid var(--wpds-color-stroke-surface-neutral); + border: var( --wpds-border-width-xs ) solid var( --wpds-color-stroke-surface-neutral ); border-radius: 12px; background: color-mix( in srgb, - var(--wpds-color-bg-surface-neutral-strong) var(--surface-alpha, 50%), + var( --wpds-color-bg-surface-neutral-strong ) var( --surface-alpha, 50% ), transparent ); - backdrop-filter: blur(12px); + backdrop-filter: blur( 12px ); appearance: none; font: inherit; text-align: center; @@ -36,20 +36,20 @@ .card:hover, .cardDragging { - border-color: var(--wpds-color-stroke-interactive-brand); - box-shadow: 0 0 0 1px var(--wpds-color-stroke-interactive-brand); + border-color: var( --wpds-color-stroke-interactive-brand ); + box-shadow: 0 0 0 1px var( --wpds-color-stroke-interactive-brand ); } .card:focus-visible { - outline: 2px solid var(--wpds-color-stroke-focus-brand); + outline: 2px solid var( --wpds-color-stroke-focus-brand ); outline-offset: 2px; - border-color: var(--wpds-color-stroke-interactive-brand); + border-color: var( --wpds-color-stroke-interactive-brand ); } .cardDisabled, .cardDisabled:hover { opacity: 0.55; - border-color: var(--wpds-color-stroke-surface-neutral); + border-color: var( --wpds-color-stroke-surface-neutral ); box-shadow: none; cursor: not-allowed; } @@ -61,41 +61,41 @@ } .cardTitle { - font-size: var(--wpds-typography-font-size-lg); + font-size: var( --wpds-typography-font-size-lg ); font-weight: 500; transition: color 0.15s ease; } .card:hover .cardTitle, .card:focus-visible .cardTitle { - color: var(--wpds-color-fg-interactive-brand); + color: var( --wpds-color-fg-interactive-brand ); } .cardBody { margin: 0; - color: var(--wpds-color-fg-content-neutral-weak); - font-size: var(--wpds-typography-font-size-md); + color: var( --wpds-color-fg-content-neutral-weak ); + font-size: var( --wpds-typography-font-size-md ); line-height: 1.4; } .cardHint { - color: var(--wpds-color-fg-content-neutral-weak); - font-size: var(--wpds-typography-font-size-sm); + color: var( --wpds-color-fg-content-neutral-weak ); + font-size: var( --wpds-typography-font-size-sm ); } .cardError { margin-top: 4px; - color: var(--wpds-color-fg-content-error); - font-size: var(--wpds-typography-font-size-sm); + color: var( --wpds-color-fg-content-error ); + font-size: var( --wpds-typography-font-size-sm ); } .hiddenInput { display: none; } -@media (max-width: 720px) { +@media ( max-width: 720px ) { .cards { - grid-template-columns: minmax(0, 1fr); + grid-template-columns: minmax( 0, 1fr ); } .card { From 2474ac3f297d8dd764d67dde302126c151735b23 Mon Sep 17 00:00:00 2001 From: Shaun Andrews Date: Mon, 31 Aug 2026 20:22:26 -0400 Subject: [PATCH 2/2] Remove Blueprint preview cropping --- .../components/blueprint-gallery/index.tsx | 21 ++----------------- .../blueprint-gallery/style.module.css | 8 ------- .../router/route-onboarding-create/index.tsx | 11 +--------- .../route-onboarding-create/style.module.css | 8 ------- 4 files changed, 3 insertions(+), 45 deletions(-) diff --git a/apps/ui/src/components/blueprint-gallery/index.tsx b/apps/ui/src/components/blueprint-gallery/index.tsx index 3e1358639d..4d811d0e44 100644 --- a/apps/ui/src/components/blueprint-gallery/index.tsx +++ b/apps/ui/src/components/blueprint-gallery/index.tsx @@ -11,7 +11,6 @@ import type { SelectedBlueprint } from '@/lib/blueprint-selection'; import type { Blueprint } from '@studio/common/lib/studio-blueprints-api'; import type { BlueprintV1Declaration } from '@wp-playground/blueprints'; -const BLUEPRINT_ARTWORK_SLUGS = new Set( [ 'woo-shop', 'development', 'quick-start' ] ); const SEARCH_THRESHOLD = 20; const BLUEPRINT_DISPLAY_NAMES: Record< string, string > = { @@ -71,27 +70,12 @@ function apiToSelectedBlueprint( bp: Blueprint ): SelectedBlueprint { }; } -function BlueprintCard( { - blueprint, - onClick, - cropAdminBar = false, -}: { - blueprint: Blueprint; - onClick: () => void; - cropAdminBar?: boolean; -} ) { +function BlueprintCard( { blueprint, onClick }: { blueprint: Blueprint; onClick: () => void } ) { return (