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
1 change: 1 addition & 0 deletions apps/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@tanstack/react-query": "^5.75.5",
"@tanstack/react-query-persist-client": "^5.96.2",
"@tanstack/react-router": "^1.120.14",
"@wordpress/a11y": "^4.47.0",
"@wordpress/api-fetch": "^7.47.0",
"@wordpress/components": "^34.0.0",
"@wordpress/core-data": "^7.47.0",
Expand Down
49 changes: 40 additions & 9 deletions apps/ui/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -112,19 +112,22 @@ textarea,

/* Show the focus ring only on keyboard focus, overriding @wordpress/ui's
:focus-based outline. Unlayered CSS wins over WP UI's cascade layers.
`a` covers Button primitives rendered as links via its `render` prop.
Also revert the background/border shift @wordpress/ui's Button applies on
`:focus` — when a Dialog auto-focuses a button on open, the "active" look
would otherwise show without any user intent. Leave `color` alone:
loading buttons rely on `color: transparent` to hide their label while
the spinner runs, and overriding that here would make the label visible
again. */
`a` covers Button primitives rendered as links via its `render` prop. */
button:focus:not( :focus-visible ),
a:focus:not( :focus-visible ),
[role='button']:focus:not( :focus-visible ) {
outline: none;
background-color: var( --wp-ui-button-background-color );
border-color: var( --wp-ui-button-border-color );
}

/* Revert the background/border shift @wordpress/ui's Button applies on
`:focus` without overriding unlayered custom component styles. */
@layer wp-ui-overrides {
button:focus:not( :focus-visible ),
a:focus:not( :focus-visible ),
[role='button']:focus:not( :focus-visible ) {
background-color: var( --wp-ui-button-background-color );
border-color: var( --wp-ui-button-border-color );
}
}

/* Compact density: shrink icons to match the tighter spacing tokens.
Expand All @@ -137,6 +140,34 @@ a:focus:not( :focus-visible ),
height: 16px;
}

@keyframes view-transition-exit {
to {
opacity: 0;
}
}

@keyframes view-transition-enter {
from {
opacity: 0;
transform: translateY(10px);
}
}

::view-transition-old(root) {
animation: 120ms ease-out both view-transition-exit;
}

::view-transition-new(root) {
animation: 220ms ease-out both view-transition-enter;
}

@media (prefers-reduced-motion: reduce) {
::view-transition-old(root),
::view-transition-new(root) {
animation: none;
}
}

/* Desks uses each shape's indicator as the visible selection outline.
Hide tldraw's default selection box and handle glyphs while keeping the
underlying resize/rotate wrappers interactive. */
Expand Down
39 changes: 37 additions & 2 deletions apps/ui/src/ui-classic/router/layout-onboarding/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { createRoute, Outlet, useMatches, useNavigate } from '@tanstack/react-router';
import { createRoute, Outlet, useLocation, useMatches, useNavigate } from '@tanstack/react-router';
import { useEffect, useRef } from 'react';
import { OnboardingLayout } from '@/components/onboarding-layout';
import { useSites } from '@/data/queries/use-sites';
import { rootRoute } from '../layout-root';
import styles from './style.module.css';

function OnboardingShell() {
const navigate = useNavigate();
Expand All @@ -20,12 +22,45 @@ function OnboardingShell() {
const step = ( match.search as { step?: string } ).step;
return step !== 'configure';
} );
const contentRef = useRef< HTMLDivElement >( null );
const { href } = useLocation();
const lastHref = useRef( href );
useEffect( () => {
if ( lastHref.current === href ) {
return;
}
lastHref.current = href;
const focusHeading = () => {
const content = contentRef.current;
if ( ! content ) {
return false;
}
// A page that claims focus itself wins over the default heading focus.
const active = document.activeElement;
if ( active && active !== document.body && content.contains( active ) ) {
return true;
}
const heading = content.querySelector( 'h1' );
if ( ! heading ) {
return false;
}
heading.setAttribute( 'tabindex', '-1' );
heading.focus();
return true;
};
if ( ! focusHeading() ) {
const raf = requestAnimationFrame( () => void focusHeading() );
return () => cancelAnimationFrame( raf );
}
}, [ href ] );
return (
<OnboardingLayout
onClose={ hasSites ? () => void navigate( { to: '/' } ) : undefined }
width={ isWide ? 'wide' : 'default' }
>
<Outlet />
<div ref={ contentRef } className={ styles.outlet }>
<Outlet />
</div>
</OnboardingLayout>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@
margin: 0 0 32px;
}

.outlet {
display: contents;
}

.outlet h1:focus:not(:focus-visible) {
outline: none;
}

.cards {
display: flex;
gap: 16px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import {
updateBlueprintWithFormValues,
} from '@studio/common/lib/blueprint-settings';
import { createRoute, useNavigate } from '@tanstack/react-router';
import { __ } from '@wordpress/i18n';
import { speak } from '@wordpress/a11y';
import { __, sprintf } from '@wordpress/i18n';
import { arrowLeft } from '@wordpress/icons';
import { Button, Icon } from '@wordpress/ui';
import { useCallback, useEffect, useState } from 'react';
Expand Down Expand Up @@ -109,6 +110,13 @@ function OnboardingBlueprintPage() {
filePath: picked.filePath,
},
} );
speak(
sprintf(
// translators: %s is the site name.
__( '%s site added.' ),
values.name
)
);
await navigate( { to: '/sites/$siteId/new', params: { siteId: site.id } } );
} catch ( error ) {
setSubmitError(
Expand Down
10 changes: 9 additions & 1 deletion apps/ui/src/ui-classic/router/route-onboarding-create/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createRoute, useNavigate } from '@tanstack/react-router';
import { __ } from '@wordpress/i18n';
import { speak } from '@wordpress/a11y';
import { __, sprintf } from '@wordpress/i18n';
import { useState } from 'react';
import { CreateSiteForm } from '@/components/create-site-form';
import {
Expand Down Expand Up @@ -33,6 +34,13 @@ function CreateSitePage() {
adminPassword: values.adminPassword || undefined,
adminEmail: values.adminEmail || undefined,
} );
speak(
sprintf(
// translators: %s is the site name.
__( '%s site added.' ),
values.name
)
);
await navigate( { to: '/sites/$siteId/new', params: { siteId: site.id } } );
} catch ( error ) {
setSubmitError(
Expand Down
10 changes: 9 additions & 1 deletion apps/ui/src/ui-classic/router/route-onboarding-import/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ACCEPTED_IMPORT_FILE_TYPES } from '@studio/common/constants';
import { createRoute, useNavigate } from '@tanstack/react-router';
import { __ } from '@wordpress/i18n';
import { speak } from '@wordpress/a11y';
import { __, sprintf } from '@wordpress/i18n';
import { arrowLeft, download } from '@wordpress/icons';
import { Button, Icon } from '@wordpress/ui';
import { useCallback, useEffect, useState } from 'react';
Expand Down Expand Up @@ -143,6 +144,13 @@ function OnboardingImportPage() {
siteId: site.id,
backup: { path: picked.path, type: picked.file.type },
} );
speak(
sprintf(
// translators: %s is the site name.
__( '%s site added.' ),
values.name
)
);
await navigate( { to: '/sites/$siteId/new', params: { siteId: site.id } } );
} catch ( error ) {
setSubmitError(
Expand Down
1 change: 1 addition & 0 deletions apps/ui/src/ui-classic/router/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export function createAppRouter( context: RouterContext ) {
routeTree,
context,
defaultPreload: 'intent',
defaultViewTransition: true,
history: createPackagedRouterHistory(),
} );
}
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.