diff --git a/packages/docs/src/components/Playground/DynamicPlayground.tsx b/packages/docs/src/components/Playground/DynamicPlayground.tsx
index 7fbb0ca11..96bba3975 100644
--- a/packages/docs/src/components/Playground/DynamicPlayground.tsx
+++ b/packages/docs/src/components/Playground/DynamicPlayground.tsx
@@ -10,7 +10,12 @@ import { lazy, Suspense, useEffect, useState } from 'react';
import { QueryParamProvider } from 'use-query-params';
import { WindowHistoryAdapter } from 'use-query-params/adapters/window';
import * as stylex from '@stylexjs/stylex';
-import { vars } from '@/theming/vars.stylex';
+import { vars, playgroundVars } from '@/theming/vars.stylex';
+
+const shimmer = stylex.keyframes({
+ '0%': { backgroundPosition: '200% 0' },
+ '100%': { backgroundPosition: '-200% 0' },
+});
export function ClientOnly({
children,
@@ -52,16 +57,178 @@ export function Playground() {
}
function PlaygroundPlaceholder() {
- // TODO: Add a better placeholder with a shimmer version of the playground layout
- return
Loading...
;
+ return (
+
+ );
+}
+
+function SkeletonEditorPanel({
+ tabCount,
+ lineCount,
+}: {
+ tabCount: number;
+ lineCount: number;
+}) {
+ const lineWidths = [
+ '80%',
+ '55%',
+ '70%',
+ '40%',
+ '85%',
+ '60%',
+ '50%',
+ '75%',
+ '35%',
+ ];
+ return (
+
+
+ {Array.from({ length: tabCount }).map((_, i) => (
+
+ ))}
+
+
+ {Array.from({ length: lineCount }).map((_, i) => (
+
+ ))}
+
+
+ );
+}
+
+function SkeletonPreviewPanel() {
+ return (
+
+ );
}
const styles = stylex.create({
placeholder: {
+ boxSizing: 'border-box',
+ width: '100%',
+ height: `calc(100dvh - ${vars['--fd-nav-height']})`,
+ padding: 10,
+ containerType: 'inline-size',
+ },
+ frame: {
+ boxSizing: 'border-box',
+ width: '100%',
+ height: '100%',
+ overflow: 'hidden',
+ borderColor: playgroundVars['--pg-border'],
+ borderStyle: 'solid',
+ borderWidth: 1,
+ borderRadius: 20,
+ },
+ row: {
display: 'flex',
+ flexDirection: { default: 'row', '@container (width < 768px)': 'column' },
+ width: '100%',
+ height: '100%',
+ },
+ column: {
+ display: 'flex',
+ flexGrow: 1,
+ flexBasis: 0,
+ flexDirection: 'column',
+ minWidth: 0,
+ minHeight: 0,
+ },
+ panel: {
+ position: 'relative',
+ display: 'flex',
+ flexGrow: 1,
+ flexShrink: 0,
+ flexBasis: 42,
+ flexDirection: 'column',
+ overflow: 'hidden',
+ backgroundColor: playgroundVars['--pg-panel-surface'],
+ boxShadow: `0 0 0 1px ${playgroundVars['--pg-panel-shadow']}`,
+ },
+ previewPanel: {
+ backgroundColor: playgroundVars['--pg-preview'],
+ },
+ panelHeader: {
+ display: 'flex',
+ flexShrink: 0,
+ gap: 10,
alignItems: 'center',
- justifyContent: 'center',
width: '100%',
- height: `calc(100dvh - ${vars['--fd-nav-height']})`,
+ height: 44,
+ paddingInline: 16,
+ backgroundColor: playgroundVars['--pg-header-surface'],
+ borderBottomColor: vars['--color-fd-border'],
+ borderBottomStyle: 'solid',
+ borderBottomWidth: 1,
+ },
+ tabPill: {
+ width: 64,
+ height: 16,
+ borderRadius: 4,
+ },
+ headerLabel: {
+ width: 72,
+ height: 14,
+ borderRadius: 4,
+ },
+ panelBody: {
+ display: 'flex',
+ flexGrow: 1,
+ flexDirection: 'column',
+ gap: 12,
+ padding: 18,
+ overflow: 'hidden',
+ },
+ codeLine: {
+ height: 10,
+ borderRadius: 4,
+ },
+ previewBody: {
+ display: 'flex',
+ flexGrow: 1,
+ padding: 18,
+ },
+ previewBlock: {
+ flexGrow: 1,
+ width: '100%',
+ borderRadius: 8,
+ },
+ shimmer: {
+ backgroundColor: vars['--color-fd-muted'],
+ backgroundImage: `linear-gradient(90deg, transparent 0%, ${vars['--color-fd-accent']} 50%, transparent 100%)`,
+ backgroundRepeat: 'no-repeat',
+ backgroundSize: '200% 100%',
+ animationName: shimmer,
+ animationDuration: '1.6s',
+ animationTimingFunction: 'linear',
+ animationIterationCount: 'infinite',
},
});