diff --git a/examples/ai-transport-message-per-response/javascript/index.html b/examples/ai-transport-message-per-response/javascript/index.html index b002b18e24..7276edbe7b 100644 --- a/examples/ai-transport-message-per-response/javascript/index.html +++ b/examples/ai-transport-message-per-response/javascript/index.html @@ -7,15 +7,15 @@
The Ably client has been successfully initialized.
Press the Connect button to initialize the client:
Click the button below to add some historical bids.
+
Auction item #1
No bids yet.
+ class="uk-btn uk-btn-default uk-btn-sm mr-2 border rounded-[1998px] border-black dark:border-gray-600 bg-transparent text-black dark:text-white"> Cancel - + - Auction item #1 + Auction item #1 {currentBid ? ( - + Current Bid @@ -183,7 +183,7 @@ export default function AuctionRoom() { {bid.clientId} {bid.clientId === currentClientId ? ' (You)' : ''} - + {bid.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' })} ${bid.amount} @@ -208,7 +208,7 @@ export default function AuctionRoom() { diff --git a/examples/pub-sub-history/react/src/Home.tsx b/examples/pub-sub-history/react/src/Home.tsx index aba43c70d0..0a5d4e2b6b 100644 --- a/examples/pub-sub-history/react/src/Home.tsx +++ b/examples/pub-sub-history/react/src/Home.tsx @@ -38,8 +38,8 @@ export default function Home() { }; return ( - - + + Welcome to the auction Click the button below to add some historical bids. diff --git a/examples/pub-sub-history/react/src/styles/styles.css b/examples/pub-sub-history/react/src/styles/styles.css index 9dd5977c56..c59ac476cf 100644 --- a/examples/pub-sub-history/react/src/styles/styles.css +++ b/examples/pub-sub-history/react/src/styles/styles.css @@ -69,6 +69,10 @@ height: 100vh; } +.dark .container { + background-color: hsl(var(--background)); +} + input { padding: 0.5rem; width: 100%; diff --git a/examples/pub-sub-message-annotations/javascript/index.html b/examples/pub-sub-message-annotations/javascript/index.html index 9dccae7e19..f4eaaf3797 100644 --- a/examples/pub-sub-message-annotations/javascript/index.html +++ b/examples/pub-sub-message-annotations/javascript/index.html @@ -13,7 +13,7 @@ - + Publish diff --git a/examples/pub-sub-message-annotations/javascript/src/components/annotations.ts b/examples/pub-sub-message-annotations/javascript/src/components/annotations.ts index 7283aa2d00..7d1b464780 100644 --- a/examples/pub-sub-message-annotations/javascript/src/components/annotations.ts +++ b/examples/pub-sub-message-annotations/javascript/src/components/annotations.ts @@ -27,7 +27,7 @@ function createAnnotationItem(annotation: Annotation) { const { color, label } = findAnnotationType(typeKey); const item = document.createElement('div'); - item.className = `pl-3 pr-2 py-2 border-l-4 border-l-${color}-500 border-y border-r border-gray-200 bg-white shadow-sm flex flex-wrap items-center`; + item.className = `pl-3 pr-2 py-2 border-l-4 border-l-${color}-500 border-y border-r border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 shadow-sm flex flex-wrap items-center`; item.setAttribute('data-id', annotation.id); item.setAttribute('data-timestamp', annotation.timestamp.toString()); item.setAttribute('data-serial', annotation.messageSerial); @@ -41,12 +41,12 @@ function createAnnotationItem(annotation: Annotation) { leftContent.className = 'flex items-center gap-2 min-w-0 flex-grow'; const typeLabel = document.createElement('span'); - typeLabel.className = `text-sm font-medium text-${color}-800`; + typeLabel.className = `text-sm font-medium text-${color}-800 dark:text-${color}-200`; typeLabel.textContent = label; leftContent.appendChild(typeLabel); const valueContent = document.createElement('span'); - valueContent.className = 'text-sm text-gray-700 overflow-hidden text-ellipsis'; + valueContent.className = 'text-sm text-gray-700 dark:text-gray-300 overflow-hidden text-ellipsis'; valueContent.textContent = annotation.name || 'unknown'; leftContent.appendChild(valueContent); @@ -86,7 +86,7 @@ function createAnnotationItem(annotation: Annotation) { rightContent.appendChild(clientBadge); const timestamp = document.createElement('div'); - timestamp.className = 'text-xs text-gray-500'; + timestamp.className = 'text-xs text-gray-500 dark:text-gray-400'; timestamp.textContent = formatTimestamp(annotation.timestamp); rightContent.appendChild(timestamp); @@ -106,7 +106,7 @@ export function createAnnotationsListElement(messageSerial: string) { annotationsList.setAttribute('data-message-serial', messageSerial); const emptyState = document.createElement('div'); - emptyState.className = 'text-center p-2 text-gray-500 text-sm'; + emptyState.className = 'text-center p-2 text-gray-500 dark:text-gray-400 text-sm'; emptyState.textContent = 'No annotations received yet.'; emptyState.id = `annotations-empty-${messageSerial}`; diff --git a/examples/pub-sub-message-annotations/javascript/src/components/details.ts b/examples/pub-sub-message-annotations/javascript/src/components/details.ts index 104987c9b3..47ffc88597 100644 --- a/examples/pub-sub-message-annotations/javascript/src/components/details.ts +++ b/examples/pub-sub-message-annotations/javascript/src/components/details.ts @@ -6,17 +6,17 @@ import { createAnnotationsListElement } from './annotations'; export function createPublishAnnotationElement(message: MessageWithSerial) { const publisher = document.createElement('div'); - publisher.className = 'p-2 border-b border-gray-200 bg-gray-50'; + publisher.className = 'p-2 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900'; publisher.innerHTML = ` - + total.v1 distinct.v1 unique.v1 multiple.v1 flag.v1 - + Publish `; @@ -116,7 +116,7 @@ export function createMessageDetailsElement(message: MessageWithSerial) { export function createDetailsPane(message: MessageWithSerial) { const detailsPane = document.createElement('div'); - detailsPane.className = 'border-b border-l border-r border-gray-200 rounded-b-md overflow-hidden bg-white shadow-sm'; + detailsPane.className = 'border-b border-l border-r border-gray-200 dark:border-gray-700 rounded-b-md overflow-hidden bg-white dark:bg-gray-900 shadow-sm'; detailsPane.id = `details-${message.id}`; const messageDetails = createMessageDetailsElement(message); diff --git a/examples/pub-sub-message-annotations/javascript/src/components/message.ts b/examples/pub-sub-message-annotations/javascript/src/components/message.ts index cab5dd7af5..cc2f22b590 100644 --- a/examples/pub-sub-message-annotations/javascript/src/components/message.ts +++ b/examples/pub-sub-message-annotations/javascript/src/components/message.ts @@ -17,7 +17,7 @@ export function createMessageElement(message: MessageWithSerial) { // Main message element that can be clicked to expand const messageElement = document.createElement('div'); - messageElement.className = `px-3 py-2 border-t border-l border-r border-gray-200 rounded-t-md bg-white shadow-sm cursor-pointer`; + messageElement.className = `px-3 py-2 border-t border-l border-r border-gray-200 dark:border-gray-700 rounded-t-md bg-white dark:bg-gray-900 shadow-sm cursor-pointer`; messageElement.id = `message-${message.id}`; // First row: message text (left aligned) and dropdown arrow (right aligned) @@ -25,7 +25,7 @@ export function createMessageElement(message: MessageWithSerial) { firstRow.className = 'flex justify-between items-center w-full'; const messageContent = document.createElement('div'); - messageContent.className = 'flex-grow text-sm font-medium text-gray-700 overflow-hidden text-ellipsis'; + messageContent.className = 'flex-grow text-sm font-medium text-gray-700 dark:text-gray-300 overflow-hidden text-ellipsis'; messageContent.textContent = message.data; firstRow.appendChild(messageContent); @@ -44,7 +44,7 @@ export function createMessageElement(message: MessageWithSerial) { clientBadge.classList.add('shrink-0'); const timestamp = document.createElement('div'); - timestamp.className = 'text-xs text-gray-500'; + timestamp.className = 'text-xs text-gray-500 dark:text-gray-400'; timestamp.textContent = formatTimestamp(message.timestamp || Date.now()); secondRow.appendChild(clientBadge); diff --git a/examples/pub-sub-message-annotations/javascript/src/components/summary.ts b/examples/pub-sub-message-annotations/javascript/src/components/summary.ts index 00066e2cc8..513a4f3841 100644 --- a/examples/pub-sub-message-annotations/javascript/src/components/summary.ts +++ b/examples/pub-sub-message-annotations/javascript/src/components/summary.ts @@ -14,7 +14,7 @@ import { getAnnotationTypeKey } from './annotations'; function createEmptyAnnotationSummaryContentElement() { const emptyState = document.createElement('div'); - emptyState.className = 'text-center py-2 text-gray-500 text-sm'; + emptyState.className = 'text-center py-2 text-gray-500 dark:text-gray-400 text-sm'; emptyState.textContent = 'Publish an annotation to view summaries.'; return emptyState; } @@ -34,7 +34,7 @@ function createLabelContainer(label: string, color: string) { container.className = 'flex items-center'; const labelSpan = document.createElement('span'); - labelSpan.className = `text-sm font-medium text-${color}-800`; + labelSpan.className = `text-sm font-medium text-${color}-800 dark:text-${color}-200`; labelSpan.textContent = label; container.appendChild(labelSpan); @@ -45,7 +45,7 @@ function createSectionHeader(key: string, entry: Ably.SummaryEntry) { const typeKey = getAnnotationTypeKey(key); const { color, label } = findAnnotationType(typeKey); const sectionHeader = document.createElement('div'); - sectionHeader.className = `border-l-4 border-l-${color}-500 border-t border-r border-gray-200 bg-white shadow-sm px-3 py-1.5 ${typeKey === 'total.v1' ? '' : 'cursor-pointer'}`; + sectionHeader.className = `border-l-4 border-l-${color}-500 border-t border-r border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 shadow-sm px-3 py-1.5 ${typeKey === 'total.v1' ? '' : 'cursor-pointer'}`; const wrapper = document.createElement('div'); wrapper.className = 'flex justify-between items-center'; @@ -66,7 +66,7 @@ function createSectionHeader(key: string, entry: Ably.SummaryEntry) { labelContainer.appendChild(createCountBadge(count, color)); const valueLabel = document.createElement('span'); - valueLabel.className = `ml-1 text-xs text-gray-600`; + valueLabel.className = `ml-1 text-xs text-gray-600 dark:text-gray-300`; labelContainer.appendChild(valueLabel); wrapper.appendChild(labelContainer); @@ -79,7 +79,7 @@ function createSectionHeader(key: string, entry: Ably.SummaryEntry) { function createFlagCard(entry: Ably.SummaryClientIdList, color: string) { const card = document.createElement('div'); - card.className = 'p-3 bg-white flex items-center gap-2'; + card.className = 'p-3 bg-white dark:bg-gray-900 flex items-center gap-2'; // Add empty spacer for alignment const spacer = document.createElement('span'); @@ -101,11 +101,11 @@ function createFlagCard(entry: Ably.SummaryClientIdList, color: string) { function createDistinctUniqueCard(value: string, entry: Ably.SummaryClientIdList, color: string) { const card = document.createElement('div'); - card.className = 'p-3 bg-white flex items-center gap-2'; + card.className = 'p-3 bg-white dark:bg-gray-900 flex items-center gap-2'; // Add value text const valueContent = document.createElement('span'); - valueContent.className = 'text-sm text-gray-700 flex-shrink-0 mr-auto'; + valueContent.className = 'text-sm text-gray-700 dark:text-gray-300 flex-shrink-0 mr-auto'; valueContent.textContent = value; card.appendChild(valueContent); @@ -124,11 +124,11 @@ function createDistinctUniqueCard(value: string, entry: Ably.SummaryClientIdList function createMultipleCard(value: string, entry: Ably.SummaryMultipleValues[string], color: string) { const card = document.createElement('div'); - card.className = 'p-3 bg-white flex items-center gap-2'; + card.className = 'p-3 bg-white dark:bg-gray-900 flex items-center gap-2'; // Add value text const valueContent = document.createElement('span'); - valueContent.className = 'text-sm text-gray-700 flex-shrink-0 mr-auto'; + valueContent.className = 'text-sm text-gray-700 dark:text-gray-300 flex-shrink-0 mr-auto'; valueContent.textContent = value; card.appendChild(valueContent); @@ -156,7 +156,7 @@ function createSectionContent(fullTypeKey: string, entry: Ably.SummaryEntry) { const typeKey = getAnnotationTypeKey(fullTypeKey); const { color } = findAnnotationType(typeKey); const sectionContent = document.createElement('div'); - sectionContent.className = `overflow-hidden border-l-4 border-l-${color}-500 border-b border-r border-gray-200`; + sectionContent.className = `overflow-hidden border-l-4 border-l-${color}-500 border-b border-r border-gray-200 dark:border-gray-700`; if (typeKey === 'flag.v1') { const card = createFlagCard(entry as Ably.SummaryClientIdList, color); @@ -167,7 +167,7 @@ function createSectionContent(fullTypeKey: string, entry: Ably.SummaryEntry) { const [value, info] = entryData; const card = createDistinctUniqueCard(value, info, color); if (index < entries.length - 1) { - card.classList.add('border-b', 'border-gray-100'); + card.classList.add('border-b', 'border-gray-100', 'dark:border-gray-700'); } sectionContent.appendChild(card); }); @@ -177,7 +177,7 @@ function createSectionContent(fullTypeKey: string, entry: Ably.SummaryEntry) { const [value, info] = entryData; const card = createMultipleCard(value, info, color); if (index < entries.length - 1) { - card.classList.add('border-b', 'border-gray-100'); + card.classList.add('border-b', 'border-gray-100', 'dark:border-gray-700'); } sectionContent.appendChild(card); }); diff --git a/examples/pub-sub-message-annotations/javascript/src/styles.css b/examples/pub-sub-message-annotations/javascript/src/styles.css index 562e9ac776..da6e6287c9 100644 --- a/examples/pub-sub-message-annotations/javascript/src/styles.css +++ b/examples/pub-sub-message-annotations/javascript/src/styles.css @@ -69,6 +69,10 @@ height: 100vh; } +.dark .container { + background-color: hsl(var(--background)); +} + .inner { width: 100%; max-width: 320px; diff --git a/examples/pub-sub-message-encryption/javascript/index.html b/examples/pub-sub-message-encryption/javascript/index.html index c99f196112..edb945158f 100644 --- a/examples/pub-sub-message-encryption/javascript/index.html +++ b/examples/pub-sub-message-encryption/javascript/index.html @@ -15,7 +15,7 @@ Client without an encr Publish - + diff --git a/examples/pub-sub-message-encryption/react/src/App.tsx b/examples/pub-sub-message-encryption/react/src/App.tsx index f82a97380c..a186bc3899 100644 --- a/examples/pub-sub-message-encryption/react/src/App.tsx +++ b/examples/pub-sub-message-encryption/react/src/App.tsx @@ -50,10 +50,10 @@ function EncodedMessages() { - + {messages.map((msg, index) => ( - + {msg} ))} @@ -101,10 +101,10 @@ function DecodedMessages() { - + {messages.map((msg, index) => ( - + {msg.data} ))} diff --git a/examples/pub-sub-occupancy/javascript/index.html b/examples/pub-sub-occupancy/javascript/index.html index 1e2796d16e..fda901869b 100644 --- a/examples/pub-sub-occupancy/javascript/index.html +++ b/examples/pub-sub-occupancy/javascript/index.html @@ -11,7 +11,7 @@ - + diff --git a/examples/pub-sub-occupancy/javascript/src/styles.css b/examples/pub-sub-occupancy/javascript/src/styles.css index 94b4f84544..32d359adfa 100644 --- a/examples/pub-sub-occupancy/javascript/src/styles.css +++ b/examples/pub-sub-occupancy/javascript/src/styles.css @@ -69,6 +69,10 @@ height: 100vh; } +.dark .container { + background-color: hsl(var(--background)); +} + .uk-text-primary { color: hsl(var(--primary)); } diff --git a/examples/pub-sub-occupancy/react/src/App.tsx b/examples/pub-sub-occupancy/react/src/App.tsx index 8bacee4da9..cccba11f3d 100644 --- a/examples/pub-sub-occupancy/react/src/App.tsx +++ b/examples/pub-sub-occupancy/react/src/App.tsx @@ -23,7 +23,7 @@ function Stream() { return ( - + - + - + {presenceData.map((viewer) => ( @@ -28,7 +28,7 @@ function PresenceList() { {viewer.clientId} {viewer.clientId === currentClientId ? ' (You)' : ''}{' '} - {viewer.data} + {viewer.data} ))} diff --git a/examples/pub-sub-presence/react/src/styles/styles.css b/examples/pub-sub-presence/react/src/styles/styles.css index 9dd5977c56..c59ac476cf 100644 --- a/examples/pub-sub-presence/react/src/styles/styles.css +++ b/examples/pub-sub-presence/react/src/styles/styles.css @@ -69,6 +69,10 @@ height: 100vh; } +.dark .container { + background-color: hsl(var(--background)); +} + input { padding: 0.5rem; width: 100%; diff --git a/examples/pub-sub-rewind/javascript/index.html b/examples/pub-sub-rewind/javascript/index.html index fd836f768a..00edad565c 100644 --- a/examples/pub-sub-rewind/javascript/index.html +++ b/examples/pub-sub-rewind/javascript/index.html @@ -8,8 +8,8 @@ Pub/Sub rewind channel - - + + Live odds Simulate 4 betting odds for a sports game before attaching. @@ -19,9 +19,9 @@ Live odds - + - + Royal Knights @@ -35,20 +35,20 @@ Live odds - + Home Win 2.50 - + Draw 3.42 - + Away Win 2.87 - + Odds history diff --git a/examples/pub-sub-rewind/javascript/src/script.ts b/examples/pub-sub-rewind/javascript/src/script.ts index 5d476bd9a1..c91ed73e15 100644 --- a/examples/pub-sub-rewind/javascript/src/script.ts +++ b/examples/pub-sub-rewind/javascript/src/script.ts @@ -119,7 +119,7 @@ async function addHistoryItem(message: Message, position = 'prepend') { historyItem.id = `history-item-${message.id}`; historyItem.className = 'border-b pb-2'; const historyDiv = document.createElement('div'); - historyDiv.className = 'flex justify-between text-sm text-gray-600'; + historyDiv.className = 'flex justify-between text-sm text-gray-600 dark:text-gray-300'; historyItem.appendChild(historyDiv); const homeWin = document.createElement('span'); diff --git a/examples/pub-sub-rewind/react/src/Home.tsx b/examples/pub-sub-rewind/react/src/Home.tsx index 997408ddb1..32a58d8fed 100644 --- a/examples/pub-sub-rewind/react/src/Home.tsx +++ b/examples/pub-sub-rewind/react/src/Home.tsx @@ -67,8 +67,8 @@ export default function Home() { }; return ( - - + + Live odds Simulate 4 betting odds for a sports game before attaching. diff --git a/examples/pub-sub-rewind/react/src/Odds.tsx b/examples/pub-sub-rewind/react/src/Odds.tsx index 771cd5e7de..88ae2f8416 100644 --- a/examples/pub-sub-rewind/react/src/Odds.tsx +++ b/examples/pub-sub-rewind/react/src/Odds.tsx @@ -88,11 +88,11 @@ function LiveMatch({ channelName }: { channelName: string }) { }, [matchData, channel]); return ( - + {matchData && ( <> - + {matchData.match.homeTeam} @@ -113,19 +113,19 @@ function LiveMatch({ channelName }: { channelName: string }) { - + Home win {matchData.match.matchOdds.homeWin} - + Draw {matchData.match.matchOdds.draw} - + Away win {matchData.match.matchOdds.awayWin} @@ -133,12 +133,12 @@ function LiveMatch({ channelName }: { channelName: string }) { - + Odds history {oddsHistory.map((odds, index) => ( - + Home: {odds.match.matchOdds.homeWin} Draw: {odds.match.matchOdds.draw} Away: {odds.match.matchOdds.awayWin} diff --git a/examples/spaces-avatar-stack/javascript/src/script.ts b/examples/spaces-avatar-stack/javascript/src/script.ts index 590d571f12..a4baf046e8 100644 --- a/examples/spaces-avatar-stack/javascript/src/script.ts +++ b/examples/spaces-avatar-stack/javascript/src/script.ts @@ -123,7 +123,7 @@ async function renderAvatar(member: Member, isSelf: boolean = false): Promise = ({ user, return ( - + - + ${memberName} - ${!lockedByYou ? createLockedFieldSvg('w-4 h-4 text-gray-500').outerHTML : ''} + ${!lockedByYou ? createLockedFieldSvg('w-4 h-4 text-gray-500 dark:text-gray-400').outerHTML : ''} `; inputContainer?.appendChild(lockedDiv); @@ -157,7 +157,7 @@ async function updateComponent(componentUpdate: Lock) { inputElement.setAttribute('data-locked', 'true'); if (readOnly) { - inputElement.classList.add('cursor-not-allowed', 'bg-gray-50'); + inputElement.classList.add('cursor-not-allowed', 'bg-gray-50', 'dark:bg-gray-900'); inputElement.disabled = true; } } else { @@ -168,7 +168,7 @@ async function updateComponent(componentUpdate: Lock) { inputElement.removeAttribute('data-locked'); inputElement.style.backgroundColor = ''; - inputElement.classList.remove('cursor-not-allowed', 'bg-gray-50'); + inputElement.classList.remove('cursor-not-allowed', 'bg-gray-50', 'dark:bg-gray-900'); inputElement.disabled = false; } } diff --git a/examples/spaces-component-locking/react/src/components/InputCell.tsx b/examples/spaces-component-locking/react/src/components/InputCell.tsx index 2b63273a63..812e77b844 100644 --- a/examples/spaces-component-locking/react/src/components/InputCell.tsx +++ b/examples/spaces-component-locking/react/src/components/InputCell.tsx @@ -44,11 +44,11 @@ export function InputCell({ return ( - + {label} {memberName && ( @@ -57,7 +57,7 @@ export function InputCell({ {memberName} {!lockedByYou && ( - + )} )} @@ -71,12 +71,12 @@ export function InputCell({ placeholder="Click to lock and edit me" className={`uk-input w-full p-3 rounded-md transition-colors duration-200 ${ !lockHolder - ? 'bg-white hover:bg-gray-50' + ? 'bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-gray-900' : 'bg-opacity-10' } ${ !readOnly ? 'cursor-text focus:ring-2 focus:ring-blue-500 focus:border-blue-500' - : 'cursor-not-allowed bg-gray-50' + : 'cursor-not-allowed bg-gray-50 dark:bg-gray-900' }`} /> diff --git a/examples/spaces-component-locking/react/src/styles/styles.css b/examples/spaces-component-locking/react/src/styles/styles.css index 9dd5977c56..c59ac476cf 100644 --- a/examples/spaces-component-locking/react/src/styles/styles.css +++ b/examples/spaces-component-locking/react/src/styles/styles.css @@ -69,6 +69,10 @@ height: 100vh; } +.dark .container { + background-color: hsl(var(--background)); +} + input { padding: 0.5rem; width: 100%; diff --git a/examples/spaces-live-cursors/javascript/index.html b/examples/spaces-live-cursors/javascript/index.html index ff005910c7..12b9ee5d73 100644 --- a/examples/spaces-live-cursors/javascript/index.html +++ b/examples/spaces-live-cursors/javascript/index.html @@ -8,7 +8,7 @@ - + Move your cursor over the screen to see live cursors in action diff --git a/examples/spaces-live-cursors/react/src/App.tsx b/examples/spaces-live-cursors/react/src/App.tsx index 0677c01b63..90a6ca4d29 100644 --- a/examples/spaces-live-cursors/react/src/App.tsx +++ b/examples/spaces-live-cursors/react/src/App.tsx @@ -41,7 +41,7 @@ function LiveCursorsDemo() { Move your cursor over the screen to see live cursors in action diff --git a/examples/spaces-live-cursors/react/src/styles/styles.css b/examples/spaces-live-cursors/react/src/styles/styles.css index 9dd5977c56..c59ac476cf 100644 --- a/examples/spaces-live-cursors/react/src/styles/styles.css +++ b/examples/spaces-live-cursors/react/src/styles/styles.css @@ -69,6 +69,10 @@ height: 100vh; } +.dark .container { + background-color: hsl(var(--background)); +} + input { padding: 0.5rem; width: 100%; diff --git a/examples/spaces-member-location/javascript/index.html b/examples/spaces-member-location/javascript/index.html index 3b6fd354f9..4dac473654 100644 --- a/examples/spaces-member-location/javascript/index.html +++ b/examples/spaces-member-location/javascript/index.html @@ -9,7 +9,7 @@ - + diff --git a/examples/spaces-member-location/javascript/src/script.ts b/examples/spaces-member-location/javascript/src/script.ts index 22a6d3bc5e..704f143f5d 100644 --- a/examples/spaces-member-location/javascript/src/script.ts +++ b/examples/spaces-member-location/javascript/src/script.ts @@ -92,10 +92,10 @@ function buildCell(value: string, rowIndex: number, colIndex: number, cellMember const cellTd = document.createElement('td'); cellTd.setAttribute('key', `${rowIndex}-${colIndex}`); - cellTd.className = `uk-table-middle border border-gray-300 p-2 cursor-pointer transition-colors relative - ${selfInCell ? 'bg-white' : 'bg-gray-50'} + cellTd.className = `uk-table-middle border border-gray-300 dark:border-gray-700 p-2 cursor-pointer transition-colors relative + ${selfInCell ? 'bg-white dark:bg-gray-900' : 'bg-gray-50 dark:bg-gray-900'} ${cellMembers.length > 0 && !selfInCell ? 'uk-box-shadow-small' : ''} - hover:bg-gray-100`; + hover:bg-gray-100 dark:hover:bg-gray-800`; cellTd.addEventListener('click', () => handleClick(rowIndex, colIndex)); cellTd.textContent = value; @@ -118,7 +118,7 @@ async function buildSpreadsheet(otherMembers: Member[], self: Member) { sheetRow.className = 'uk-table-middle'; const sheetTd = document.createElement('td'); - sheetTd.className = 'uk-text-bold uk-text-center uk-background-muted border border-gray-300 p-2'; + sheetTd.className = 'uk-text-bold uk-text-center uk-background-muted border border-gray-300 dark:border-gray-700 p-2'; sheetTd.textContent = (rowIndex + 1).toString(); sheetRow.appendChild(sheetTd); diff --git a/examples/spaces-member-location/react/src/components/Cell.tsx b/examples/spaces-member-location/react/src/components/Cell.tsx index 10f7a39f44..8e4f2412cb 100644 --- a/examples/spaces-member-location/react/src/components/Cell.tsx +++ b/examples/spaces-member-location/react/src/components/Cell.tsx @@ -53,10 +53,10 @@ function Cell({ "--member-color": self?.profileData.memberColor, "--cell-member-color": cellMembers[0]?.profileData.memberColor, } as React.CSSProperties} - className={`uk-table-middle border border-gray-300 p-2 cursor-pointer transition-colors relative - ${selfInCell ? 'bg-white' : 'bg-gray-50'} + className={`uk-table-middle border border-gray-300 dark:border-gray-700 p-2 cursor-pointer transition-colors relative + ${selfInCell ? 'bg-white dark:bg-gray-900' : 'bg-gray-50 dark:bg-gray-900'} ${cellMembers.length > 0 && !selfInCell ? 'uk-box-shadow-small' : ''} - hover:bg-gray-100`} + hover:bg-gray-100 dark:hover:bg-gray-800`} onClick={() => handleClick(rowIndex, colIndex)} data-name-content={memberName ? cellLabel : ""} > diff --git a/examples/spaces-member-location/react/src/components/Spreadsheet.tsx b/examples/spaces-member-location/react/src/components/Spreadsheet.tsx index 713a510168..b5ef390d2b 100644 --- a/examples/spaces-member-location/react/src/components/Spreadsheet.tsx +++ b/examples/spaces-member-location/react/src/components/Spreadsheet.tsx @@ -32,11 +32,11 @@ const Spreadsheet = ({ }; return ( - + {cellData.map((row: string[], rowIndex: number) => ( - + {rowIndex + 1} {row.map((col, colIndex) => { diff --git a/examples/spaces-member-location/react/src/styles/styles.css b/examples/spaces-member-location/react/src/styles/styles.css index 9dd5977c56..c59ac476cf 100644 --- a/examples/spaces-member-location/react/src/styles/styles.css +++ b/examples/spaces-member-location/react/src/styles/styles.css @@ -69,6 +69,10 @@ height: 100vh; } +.dark .container { + background-color: hsl(var(--background)); +} + input { padding: 0.5rem; width: 100%; diff --git a/src/components/CodeEditor/CodeEditor.css b/src/components/CodeEditor/CodeEditor.css index e4ebedd567..b6e80800be 100644 --- a/src/components/CodeEditor/CodeEditor.css +++ b/src/components/CodeEditor/CodeEditor.css @@ -5,4 +5,11 @@ div.remove-border-radius { border-radius: 0; +} + +/* Sandpack renders the preview on a fixed white "browser" surface. In dark mode + that shows as a white panel around/behind the themed example, so flip it to + the dark canvas (neutral-1300, the inverse of white). */ +.ui-theme-dark .sp-preview-container { + background-color: var(--color-neutral-1300); } \ No newline at end of file diff --git a/src/components/Examples/ExamplesRenderer.tsx b/src/components/Examples/ExamplesRenderer.tsx index 33a4a2c135..586d670d35 100644 --- a/src/components/Examples/ExamplesRenderer.tsx +++ b/src/components/Examples/ExamplesRenderer.tsx @@ -10,6 +10,7 @@ import Icon from 'src/components/Icon'; import { IconName } from 'src/components/Icon/types'; import SegmentedControl from 'src/components/ui/SegmentedControl'; import dotGrid from './images/dot-grid.svg'; +import { withThemeSync } from './sandpackThemeSync'; import cn from 'src/utilities/cn'; import { getRandomChannelName } from '../../utilities/get-random-channel-name'; // Shared tsconfig for proper ES2020+ transpilation in Sandpack @@ -97,6 +98,15 @@ const ExamplesRenderer = ({ }, [files, apiKey]); const languageFiles = rewrittenFiles[activeLanguage]; + // Bake the current theme into the example files. Combined with the keyed + // SandpackProvider below, a theme change fully reloads the preview into the + // matching theme — a full reload (not an in-place hot reload) so the example's + // side effects, e.g. its Ably connection, are torn down rather than stacking + // up a fresh instance on each toggle. + const themedFiles = useMemo( + () => withThemeSync(languageFiles, activeLanguage, resolvedTheme), + [languageFiles, activeLanguage, resolvedTheme], + ); const isVerticalLayout = useMemo(() => layout === 'single-vertical' || layout === 'double-vertical', [layout]); const isDoubleLayout = useMemo(() => layout === 'double-horizontal' || layout === 'double-vertical', [layout]); @@ -105,8 +115,13 @@ const ExamplesRenderer = ({ return ( `// Injected by the docs site — applies the docs theme to this preview. +(function () { + if (typeof window === 'undefined') return; + // Class-based dark mode so Tailwind \`dark:\` utilities follow the class rather + // than the OS preference (the Play CDN defaults to media). + window.tailwind = window.tailwind || {}; + window.tailwind.config = Object.assign({}, window.tailwind.config, { darkMode: 'class' }); + + document.documentElement.classList.toggle('dark', ${isDark}); + document.documentElement.style.colorScheme = ${isDark ? "'dark'" : "'light'"}; + + // The examples' --primary token is identical in light and dark, so + // uk-text-primary (used for body text) would stay a low-contrast mid-slate on + // dark. Remap it to the foreground token in dark; scoped to .dark so light is + // untouched. + var fixes = document.createElement('style'); + fixes.textContent = '.dark .uk-text-primary{color:hsl(var(--foreground))}'; + (document.head || document.documentElement).appendChild(fixes); +})(); +`; + +// Entry module per template (see data/createPages: react files are stripped of +// their `src/` prefix, javascript files keep their project-relative path). Match +// on a pattern so both `index.tsx` and `main.tsx` entries (and any `src/`-prefixed +// variant) are found. +const ENTRY_PATTERN_BY_LANGUAGE: Partial> = { + react: /(^|\/)(index|main)\.tsx$/, + javascript: /(^|\/)script\.ts$/, +}; + +// Sandpack accepts either a raw code string or a { code, hidden } object per +// file (the renderer already relies on this for the injected tsconfig). +type SandpackFilesMap = Record; + +const codeOf = (file: string | { code?: string }): string => (typeof file === 'string' ? file : (file.code ?? '')); + +/** + * Return a copy of an example's files with the theme baked in: a hidden module + * (imported from the entry) applies the current theme on load, and the HTML head + * gets the class-based dark-mode config ahead of the Tailwind CDN. Regenerated + * per theme, so the preview reloads to match when the docs theme changes. + */ +export const withThemeSync = ( + files: ExampleFiles[LanguageKey], + language: LanguageKey, + resolvedTheme: 'light' | 'dark', +): SandpackFilesMap | undefined => { + if (!files) { + return files; + } + + const isDark = resolvedTheme === 'dark'; + const result = { ...files } as SandpackFilesMap; + + // Add the theme module in the entry's directory and import it, so a relative + // `./` import resolves in both templates (react's entry is at the root; the + // javascript entry lives under src/). + const entryPattern = ENTRY_PATTERN_BY_LANGUAGE[language]; + const entryKey = entryPattern ? Object.keys(result).find((key) => entryPattern.test(key)) : undefined; + if (entryKey) { + const slash = entryKey.lastIndexOf('/'); + const entryDir = slash >= 0 ? entryKey.slice(0, slash + 1) : ''; + result[`${entryDir}${THEME_SYNC_FILE}`] = { code: themeSyncModule(isDark), hidden: true }; + + const importLine = `import './${THEME_SYNC_FILE}';\n`; + const original = result[entryKey]; + const code = codeOf(original); + if (!code.includes(THEME_SYNC_FILE)) { + result[entryKey] = + typeof original === 'string' ? importLine + original : { ...(original as object), code: importLine + code }; + } + } + + // Set up the theme in the HTML , ahead of the Tailwind Play CDN and the + // bundle, so `dark:` utilities key off the class and — crucially — the page + // paints on the dark canvas from the very first frame. Without this the iframe + // shows a white flash before the JS entry runs, which is visible on the slower + // re-bundle that a language switch triggers. + const htmlKey = + 'index.html' in result ? 'index.html' : Object.keys(result).find((key) => key.replace(/^\//, '') === 'index.html'); + if (htmlKey) { + const html = codeOf(result[htmlKey]); + if (!html.includes('darkMode')) { + const headTag = + `` + + `${isDark ? '' : ''}`; + const injected = html.includes('') + ? html.replace('', `\n ${headTag}`) + : `${headTag}\n${html}`; + const originalHtml = result[htmlKey]; + result[htmlKey] = typeof originalHtml === 'string' ? injected : { ...(originalHtml as object), code: injected }; + } + } + + return result; +}; diff --git a/src/templates/examples.tsx b/src/templates/examples.tsx index bcb678c929..0900bd249c 100644 --- a/src/templates/examples.tsx +++ b/src/templates/examples.tsx @@ -61,19 +61,27 @@ const MarkdownOverrides = { ), }, thead: { - component: ({ children }: PropsWithChildren) => {children}, + component: ({ children }: PropsWithChildren) => ( + {children} + ), }, tbody: { - component: ({ children }: PropsWithChildren) => {children}, + component: ({ children }: PropsWithChildren) => ( + + {children} + + ), }, tr: { component: ({ children }: PropsWithChildren) => ( - {children} + + {children} + ), }, th: { component: ({ children }: PropsWithChildren) => ( - + {children} ),
diff --git a/examples/pub-sub-history/react/src/Home.tsx b/examples/pub-sub-history/react/src/Home.tsx index aba43c70d0..0a5d4e2b6b 100644 --- a/examples/pub-sub-history/react/src/Home.tsx +++ b/examples/pub-sub-history/react/src/Home.tsx @@ -38,8 +38,8 @@ export default function Home() { }; return ( - - + + Welcome to the auction Click the button below to add some historical bids. diff --git a/examples/pub-sub-history/react/src/styles/styles.css b/examples/pub-sub-history/react/src/styles/styles.css index 9dd5977c56..c59ac476cf 100644 --- a/examples/pub-sub-history/react/src/styles/styles.css +++ b/examples/pub-sub-history/react/src/styles/styles.css @@ -69,6 +69,10 @@ height: 100vh; } +.dark .container { + background-color: hsl(var(--background)); +} + input { padding: 0.5rem; width: 100%; diff --git a/examples/pub-sub-message-annotations/javascript/index.html b/examples/pub-sub-message-annotations/javascript/index.html index 9dccae7e19..f4eaaf3797 100644 --- a/examples/pub-sub-message-annotations/javascript/index.html +++ b/examples/pub-sub-message-annotations/javascript/index.html @@ -13,7 +13,7 @@ - + Publish diff --git a/examples/pub-sub-message-annotations/javascript/src/components/annotations.ts b/examples/pub-sub-message-annotations/javascript/src/components/annotations.ts index 7283aa2d00..7d1b464780 100644 --- a/examples/pub-sub-message-annotations/javascript/src/components/annotations.ts +++ b/examples/pub-sub-message-annotations/javascript/src/components/annotations.ts @@ -27,7 +27,7 @@ function createAnnotationItem(annotation: Annotation) { const { color, label } = findAnnotationType(typeKey); const item = document.createElement('div'); - item.className = `pl-3 pr-2 py-2 border-l-4 border-l-${color}-500 border-y border-r border-gray-200 bg-white shadow-sm flex flex-wrap items-center`; + item.className = `pl-3 pr-2 py-2 border-l-4 border-l-${color}-500 border-y border-r border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 shadow-sm flex flex-wrap items-center`; item.setAttribute('data-id', annotation.id); item.setAttribute('data-timestamp', annotation.timestamp.toString()); item.setAttribute('data-serial', annotation.messageSerial); @@ -41,12 +41,12 @@ function createAnnotationItem(annotation: Annotation) { leftContent.className = 'flex items-center gap-2 min-w-0 flex-grow'; const typeLabel = document.createElement('span'); - typeLabel.className = `text-sm font-medium text-${color}-800`; + typeLabel.className = `text-sm font-medium text-${color}-800 dark:text-${color}-200`; typeLabel.textContent = label; leftContent.appendChild(typeLabel); const valueContent = document.createElement('span'); - valueContent.className = 'text-sm text-gray-700 overflow-hidden text-ellipsis'; + valueContent.className = 'text-sm text-gray-700 dark:text-gray-300 overflow-hidden text-ellipsis'; valueContent.textContent = annotation.name || 'unknown'; leftContent.appendChild(valueContent); @@ -86,7 +86,7 @@ function createAnnotationItem(annotation: Annotation) { rightContent.appendChild(clientBadge); const timestamp = document.createElement('div'); - timestamp.className = 'text-xs text-gray-500'; + timestamp.className = 'text-xs text-gray-500 dark:text-gray-400'; timestamp.textContent = formatTimestamp(annotation.timestamp); rightContent.appendChild(timestamp); @@ -106,7 +106,7 @@ export function createAnnotationsListElement(messageSerial: string) { annotationsList.setAttribute('data-message-serial', messageSerial); const emptyState = document.createElement('div'); - emptyState.className = 'text-center p-2 text-gray-500 text-sm'; + emptyState.className = 'text-center p-2 text-gray-500 dark:text-gray-400 text-sm'; emptyState.textContent = 'No annotations received yet.'; emptyState.id = `annotations-empty-${messageSerial}`; diff --git a/examples/pub-sub-message-annotations/javascript/src/components/details.ts b/examples/pub-sub-message-annotations/javascript/src/components/details.ts index 104987c9b3..47ffc88597 100644 --- a/examples/pub-sub-message-annotations/javascript/src/components/details.ts +++ b/examples/pub-sub-message-annotations/javascript/src/components/details.ts @@ -6,17 +6,17 @@ import { createAnnotationsListElement } from './annotations'; export function createPublishAnnotationElement(message: MessageWithSerial) { const publisher = document.createElement('div'); - publisher.className = 'p-2 border-b border-gray-200 bg-gray-50'; + publisher.className = 'p-2 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900'; publisher.innerHTML = ` - + total.v1 distinct.v1 unique.v1 multiple.v1 flag.v1 - + Publish `; @@ -116,7 +116,7 @@ export function createMessageDetailsElement(message: MessageWithSerial) { export function createDetailsPane(message: MessageWithSerial) { const detailsPane = document.createElement('div'); - detailsPane.className = 'border-b border-l border-r border-gray-200 rounded-b-md overflow-hidden bg-white shadow-sm'; + detailsPane.className = 'border-b border-l border-r border-gray-200 dark:border-gray-700 rounded-b-md overflow-hidden bg-white dark:bg-gray-900 shadow-sm'; detailsPane.id = `details-${message.id}`; const messageDetails = createMessageDetailsElement(message); diff --git a/examples/pub-sub-message-annotations/javascript/src/components/message.ts b/examples/pub-sub-message-annotations/javascript/src/components/message.ts index cab5dd7af5..cc2f22b590 100644 --- a/examples/pub-sub-message-annotations/javascript/src/components/message.ts +++ b/examples/pub-sub-message-annotations/javascript/src/components/message.ts @@ -17,7 +17,7 @@ export function createMessageElement(message: MessageWithSerial) { // Main message element that can be clicked to expand const messageElement = document.createElement('div'); - messageElement.className = `px-3 py-2 border-t border-l border-r border-gray-200 rounded-t-md bg-white shadow-sm cursor-pointer`; + messageElement.className = `px-3 py-2 border-t border-l border-r border-gray-200 dark:border-gray-700 rounded-t-md bg-white dark:bg-gray-900 shadow-sm cursor-pointer`; messageElement.id = `message-${message.id}`; // First row: message text (left aligned) and dropdown arrow (right aligned) @@ -25,7 +25,7 @@ export function createMessageElement(message: MessageWithSerial) { firstRow.className = 'flex justify-between items-center w-full'; const messageContent = document.createElement('div'); - messageContent.className = 'flex-grow text-sm font-medium text-gray-700 overflow-hidden text-ellipsis'; + messageContent.className = 'flex-grow text-sm font-medium text-gray-700 dark:text-gray-300 overflow-hidden text-ellipsis'; messageContent.textContent = message.data; firstRow.appendChild(messageContent); @@ -44,7 +44,7 @@ export function createMessageElement(message: MessageWithSerial) { clientBadge.classList.add('shrink-0'); const timestamp = document.createElement('div'); - timestamp.className = 'text-xs text-gray-500'; + timestamp.className = 'text-xs text-gray-500 dark:text-gray-400'; timestamp.textContent = formatTimestamp(message.timestamp || Date.now()); secondRow.appendChild(clientBadge); diff --git a/examples/pub-sub-message-annotations/javascript/src/components/summary.ts b/examples/pub-sub-message-annotations/javascript/src/components/summary.ts index 00066e2cc8..513a4f3841 100644 --- a/examples/pub-sub-message-annotations/javascript/src/components/summary.ts +++ b/examples/pub-sub-message-annotations/javascript/src/components/summary.ts @@ -14,7 +14,7 @@ import { getAnnotationTypeKey } from './annotations'; function createEmptyAnnotationSummaryContentElement() { const emptyState = document.createElement('div'); - emptyState.className = 'text-center py-2 text-gray-500 text-sm'; + emptyState.className = 'text-center py-2 text-gray-500 dark:text-gray-400 text-sm'; emptyState.textContent = 'Publish an annotation to view summaries.'; return emptyState; } @@ -34,7 +34,7 @@ function createLabelContainer(label: string, color: string) { container.className = 'flex items-center'; const labelSpan = document.createElement('span'); - labelSpan.className = `text-sm font-medium text-${color}-800`; + labelSpan.className = `text-sm font-medium text-${color}-800 dark:text-${color}-200`; labelSpan.textContent = label; container.appendChild(labelSpan); @@ -45,7 +45,7 @@ function createSectionHeader(key: string, entry: Ably.SummaryEntry) { const typeKey = getAnnotationTypeKey(key); const { color, label } = findAnnotationType(typeKey); const sectionHeader = document.createElement('div'); - sectionHeader.className = `border-l-4 border-l-${color}-500 border-t border-r border-gray-200 bg-white shadow-sm px-3 py-1.5 ${typeKey === 'total.v1' ? '' : 'cursor-pointer'}`; + sectionHeader.className = `border-l-4 border-l-${color}-500 border-t border-r border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 shadow-sm px-3 py-1.5 ${typeKey === 'total.v1' ? '' : 'cursor-pointer'}`; const wrapper = document.createElement('div'); wrapper.className = 'flex justify-between items-center'; @@ -66,7 +66,7 @@ function createSectionHeader(key: string, entry: Ably.SummaryEntry) { labelContainer.appendChild(createCountBadge(count, color)); const valueLabel = document.createElement('span'); - valueLabel.className = `ml-1 text-xs text-gray-600`; + valueLabel.className = `ml-1 text-xs text-gray-600 dark:text-gray-300`; labelContainer.appendChild(valueLabel); wrapper.appendChild(labelContainer); @@ -79,7 +79,7 @@ function createSectionHeader(key: string, entry: Ably.SummaryEntry) { function createFlagCard(entry: Ably.SummaryClientIdList, color: string) { const card = document.createElement('div'); - card.className = 'p-3 bg-white flex items-center gap-2'; + card.className = 'p-3 bg-white dark:bg-gray-900 flex items-center gap-2'; // Add empty spacer for alignment const spacer = document.createElement('span'); @@ -101,11 +101,11 @@ function createFlagCard(entry: Ably.SummaryClientIdList, color: string) { function createDistinctUniqueCard(value: string, entry: Ably.SummaryClientIdList, color: string) { const card = document.createElement('div'); - card.className = 'p-3 bg-white flex items-center gap-2'; + card.className = 'p-3 bg-white dark:bg-gray-900 flex items-center gap-2'; // Add value text const valueContent = document.createElement('span'); - valueContent.className = 'text-sm text-gray-700 flex-shrink-0 mr-auto'; + valueContent.className = 'text-sm text-gray-700 dark:text-gray-300 flex-shrink-0 mr-auto'; valueContent.textContent = value; card.appendChild(valueContent); @@ -124,11 +124,11 @@ function createDistinctUniqueCard(value: string, entry: Ably.SummaryClientIdList function createMultipleCard(value: string, entry: Ably.SummaryMultipleValues[string], color: string) { const card = document.createElement('div'); - card.className = 'p-3 bg-white flex items-center gap-2'; + card.className = 'p-3 bg-white dark:bg-gray-900 flex items-center gap-2'; // Add value text const valueContent = document.createElement('span'); - valueContent.className = 'text-sm text-gray-700 flex-shrink-0 mr-auto'; + valueContent.className = 'text-sm text-gray-700 dark:text-gray-300 flex-shrink-0 mr-auto'; valueContent.textContent = value; card.appendChild(valueContent); @@ -156,7 +156,7 @@ function createSectionContent(fullTypeKey: string, entry: Ably.SummaryEntry) { const typeKey = getAnnotationTypeKey(fullTypeKey); const { color } = findAnnotationType(typeKey); const sectionContent = document.createElement('div'); - sectionContent.className = `overflow-hidden border-l-4 border-l-${color}-500 border-b border-r border-gray-200`; + sectionContent.className = `overflow-hidden border-l-4 border-l-${color}-500 border-b border-r border-gray-200 dark:border-gray-700`; if (typeKey === 'flag.v1') { const card = createFlagCard(entry as Ably.SummaryClientIdList, color); @@ -167,7 +167,7 @@ function createSectionContent(fullTypeKey: string, entry: Ably.SummaryEntry) { const [value, info] = entryData; const card = createDistinctUniqueCard(value, info, color); if (index < entries.length - 1) { - card.classList.add('border-b', 'border-gray-100'); + card.classList.add('border-b', 'border-gray-100', 'dark:border-gray-700'); } sectionContent.appendChild(card); }); @@ -177,7 +177,7 @@ function createSectionContent(fullTypeKey: string, entry: Ably.SummaryEntry) { const [value, info] = entryData; const card = createMultipleCard(value, info, color); if (index < entries.length - 1) { - card.classList.add('border-b', 'border-gray-100'); + card.classList.add('border-b', 'border-gray-100', 'dark:border-gray-700'); } sectionContent.appendChild(card); }); diff --git a/examples/pub-sub-message-annotations/javascript/src/styles.css b/examples/pub-sub-message-annotations/javascript/src/styles.css index 562e9ac776..da6e6287c9 100644 --- a/examples/pub-sub-message-annotations/javascript/src/styles.css +++ b/examples/pub-sub-message-annotations/javascript/src/styles.css @@ -69,6 +69,10 @@ height: 100vh; } +.dark .container { + background-color: hsl(var(--background)); +} + .inner { width: 100%; max-width: 320px; diff --git a/examples/pub-sub-message-encryption/javascript/index.html b/examples/pub-sub-message-encryption/javascript/index.html index c99f196112..edb945158f 100644 --- a/examples/pub-sub-message-encryption/javascript/index.html +++ b/examples/pub-sub-message-encryption/javascript/index.html @@ -15,7 +15,7 @@ Client without an encr Publish - + diff --git a/examples/pub-sub-message-encryption/react/src/App.tsx b/examples/pub-sub-message-encryption/react/src/App.tsx index f82a97380c..a186bc3899 100644 --- a/examples/pub-sub-message-encryption/react/src/App.tsx +++ b/examples/pub-sub-message-encryption/react/src/App.tsx @@ -50,10 +50,10 @@ function EncodedMessages() { - + {messages.map((msg, index) => ( - + {msg} ))} @@ -101,10 +101,10 @@ function DecodedMessages() { - + {messages.map((msg, index) => ( - + {msg.data} ))} diff --git a/examples/pub-sub-occupancy/javascript/index.html b/examples/pub-sub-occupancy/javascript/index.html index 1e2796d16e..fda901869b 100644 --- a/examples/pub-sub-occupancy/javascript/index.html +++ b/examples/pub-sub-occupancy/javascript/index.html @@ -11,7 +11,7 @@ - + diff --git a/examples/pub-sub-occupancy/javascript/src/styles.css b/examples/pub-sub-occupancy/javascript/src/styles.css index 94b4f84544..32d359adfa 100644 --- a/examples/pub-sub-occupancy/javascript/src/styles.css +++ b/examples/pub-sub-occupancy/javascript/src/styles.css @@ -69,6 +69,10 @@ height: 100vh; } +.dark .container { + background-color: hsl(var(--background)); +} + .uk-text-primary { color: hsl(var(--primary)); } diff --git a/examples/pub-sub-occupancy/react/src/App.tsx b/examples/pub-sub-occupancy/react/src/App.tsx index 8bacee4da9..cccba11f3d 100644 --- a/examples/pub-sub-occupancy/react/src/App.tsx +++ b/examples/pub-sub-occupancy/react/src/App.tsx @@ -23,7 +23,7 @@ function Stream() { return ( - + - + - + {presenceData.map((viewer) => ( @@ -28,7 +28,7 @@ function PresenceList() { {viewer.clientId} {viewer.clientId === currentClientId ? ' (You)' : ''}{' '} - {viewer.data} + {viewer.data} ))} diff --git a/examples/pub-sub-presence/react/src/styles/styles.css b/examples/pub-sub-presence/react/src/styles/styles.css index 9dd5977c56..c59ac476cf 100644 --- a/examples/pub-sub-presence/react/src/styles/styles.css +++ b/examples/pub-sub-presence/react/src/styles/styles.css @@ -69,6 +69,10 @@ height: 100vh; } +.dark .container { + background-color: hsl(var(--background)); +} + input { padding: 0.5rem; width: 100%; diff --git a/examples/pub-sub-rewind/javascript/index.html b/examples/pub-sub-rewind/javascript/index.html index fd836f768a..00edad565c 100644 --- a/examples/pub-sub-rewind/javascript/index.html +++ b/examples/pub-sub-rewind/javascript/index.html @@ -8,8 +8,8 @@ Pub/Sub rewind channel - - + + Live odds Simulate 4 betting odds for a sports game before attaching. @@ -19,9 +19,9 @@ Live odds - + - + Royal Knights @@ -35,20 +35,20 @@ Live odds - + Home Win 2.50 - + Draw 3.42 - + Away Win 2.87 - + Odds history diff --git a/examples/pub-sub-rewind/javascript/src/script.ts b/examples/pub-sub-rewind/javascript/src/script.ts index 5d476bd9a1..c91ed73e15 100644 --- a/examples/pub-sub-rewind/javascript/src/script.ts +++ b/examples/pub-sub-rewind/javascript/src/script.ts @@ -119,7 +119,7 @@ async function addHistoryItem(message: Message, position = 'prepend') { historyItem.id = `history-item-${message.id}`; historyItem.className = 'border-b pb-2'; const historyDiv = document.createElement('div'); - historyDiv.className = 'flex justify-between text-sm text-gray-600'; + historyDiv.className = 'flex justify-between text-sm text-gray-600 dark:text-gray-300'; historyItem.appendChild(historyDiv); const homeWin = document.createElement('span'); diff --git a/examples/pub-sub-rewind/react/src/Home.tsx b/examples/pub-sub-rewind/react/src/Home.tsx index 997408ddb1..32a58d8fed 100644 --- a/examples/pub-sub-rewind/react/src/Home.tsx +++ b/examples/pub-sub-rewind/react/src/Home.tsx @@ -67,8 +67,8 @@ export default function Home() { }; return ( - - + + Live odds Simulate 4 betting odds for a sports game before attaching. diff --git a/examples/pub-sub-rewind/react/src/Odds.tsx b/examples/pub-sub-rewind/react/src/Odds.tsx index 771cd5e7de..88ae2f8416 100644 --- a/examples/pub-sub-rewind/react/src/Odds.tsx +++ b/examples/pub-sub-rewind/react/src/Odds.tsx @@ -88,11 +88,11 @@ function LiveMatch({ channelName }: { channelName: string }) { }, [matchData, channel]); return ( - + {matchData && ( <> - + {matchData.match.homeTeam} @@ -113,19 +113,19 @@ function LiveMatch({ channelName }: { channelName: string }) { - + Home win {matchData.match.matchOdds.homeWin} - + Draw {matchData.match.matchOdds.draw} - + Away win {matchData.match.matchOdds.awayWin} @@ -133,12 +133,12 @@ function LiveMatch({ channelName }: { channelName: string }) { - + Odds history {oddsHistory.map((odds, index) => ( - + Home: {odds.match.matchOdds.homeWin} Draw: {odds.match.matchOdds.draw} Away: {odds.match.matchOdds.awayWin} diff --git a/examples/spaces-avatar-stack/javascript/src/script.ts b/examples/spaces-avatar-stack/javascript/src/script.ts index 590d571f12..a4baf046e8 100644 --- a/examples/spaces-avatar-stack/javascript/src/script.ts +++ b/examples/spaces-avatar-stack/javascript/src/script.ts @@ -123,7 +123,7 @@ async function renderAvatar(member: Member, isSelf: boolean = false): Promise = ({ user, return ( - + - + ${memberName} - ${!lockedByYou ? createLockedFieldSvg('w-4 h-4 text-gray-500').outerHTML : ''} + ${!lockedByYou ? createLockedFieldSvg('w-4 h-4 text-gray-500 dark:text-gray-400').outerHTML : ''} `; inputContainer?.appendChild(lockedDiv); @@ -157,7 +157,7 @@ async function updateComponent(componentUpdate: Lock) { inputElement.setAttribute('data-locked', 'true'); if (readOnly) { - inputElement.classList.add('cursor-not-allowed', 'bg-gray-50'); + inputElement.classList.add('cursor-not-allowed', 'bg-gray-50', 'dark:bg-gray-900'); inputElement.disabled = true; } } else { @@ -168,7 +168,7 @@ async function updateComponent(componentUpdate: Lock) { inputElement.removeAttribute('data-locked'); inputElement.style.backgroundColor = ''; - inputElement.classList.remove('cursor-not-allowed', 'bg-gray-50'); + inputElement.classList.remove('cursor-not-allowed', 'bg-gray-50', 'dark:bg-gray-900'); inputElement.disabled = false; } } diff --git a/examples/spaces-component-locking/react/src/components/InputCell.tsx b/examples/spaces-component-locking/react/src/components/InputCell.tsx index 2b63273a63..812e77b844 100644 --- a/examples/spaces-component-locking/react/src/components/InputCell.tsx +++ b/examples/spaces-component-locking/react/src/components/InputCell.tsx @@ -44,11 +44,11 @@ export function InputCell({ return ( - + {label} {memberName && ( @@ -57,7 +57,7 @@ export function InputCell({ {memberName} {!lockedByYou && ( - + )} )} @@ -71,12 +71,12 @@ export function InputCell({ placeholder="Click to lock and edit me" className={`uk-input w-full p-3 rounded-md transition-colors duration-200 ${ !lockHolder - ? 'bg-white hover:bg-gray-50' + ? 'bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-gray-900' : 'bg-opacity-10' } ${ !readOnly ? 'cursor-text focus:ring-2 focus:ring-blue-500 focus:border-blue-500' - : 'cursor-not-allowed bg-gray-50' + : 'cursor-not-allowed bg-gray-50 dark:bg-gray-900' }`} /> diff --git a/examples/spaces-component-locking/react/src/styles/styles.css b/examples/spaces-component-locking/react/src/styles/styles.css index 9dd5977c56..c59ac476cf 100644 --- a/examples/spaces-component-locking/react/src/styles/styles.css +++ b/examples/spaces-component-locking/react/src/styles/styles.css @@ -69,6 +69,10 @@ height: 100vh; } +.dark .container { + background-color: hsl(var(--background)); +} + input { padding: 0.5rem; width: 100%; diff --git a/examples/spaces-live-cursors/javascript/index.html b/examples/spaces-live-cursors/javascript/index.html index ff005910c7..12b9ee5d73 100644 --- a/examples/spaces-live-cursors/javascript/index.html +++ b/examples/spaces-live-cursors/javascript/index.html @@ -8,7 +8,7 @@ - + Move your cursor over the screen to see live cursors in action diff --git a/examples/spaces-live-cursors/react/src/App.tsx b/examples/spaces-live-cursors/react/src/App.tsx index 0677c01b63..90a6ca4d29 100644 --- a/examples/spaces-live-cursors/react/src/App.tsx +++ b/examples/spaces-live-cursors/react/src/App.tsx @@ -41,7 +41,7 @@ function LiveCursorsDemo() { Move your cursor over the screen to see live cursors in action diff --git a/examples/spaces-live-cursors/react/src/styles/styles.css b/examples/spaces-live-cursors/react/src/styles/styles.css index 9dd5977c56..c59ac476cf 100644 --- a/examples/spaces-live-cursors/react/src/styles/styles.css +++ b/examples/spaces-live-cursors/react/src/styles/styles.css @@ -69,6 +69,10 @@ height: 100vh; } +.dark .container { + background-color: hsl(var(--background)); +} + input { padding: 0.5rem; width: 100%; diff --git a/examples/spaces-member-location/javascript/index.html b/examples/spaces-member-location/javascript/index.html index 3b6fd354f9..4dac473654 100644 --- a/examples/spaces-member-location/javascript/index.html +++ b/examples/spaces-member-location/javascript/index.html @@ -9,7 +9,7 @@ - + diff --git a/examples/spaces-member-location/javascript/src/script.ts b/examples/spaces-member-location/javascript/src/script.ts index 22a6d3bc5e..704f143f5d 100644 --- a/examples/spaces-member-location/javascript/src/script.ts +++ b/examples/spaces-member-location/javascript/src/script.ts @@ -92,10 +92,10 @@ function buildCell(value: string, rowIndex: number, colIndex: number, cellMember const cellTd = document.createElement('td'); cellTd.setAttribute('key', `${rowIndex}-${colIndex}`); - cellTd.className = `uk-table-middle border border-gray-300 p-2 cursor-pointer transition-colors relative - ${selfInCell ? 'bg-white' : 'bg-gray-50'} + cellTd.className = `uk-table-middle border border-gray-300 dark:border-gray-700 p-2 cursor-pointer transition-colors relative + ${selfInCell ? 'bg-white dark:bg-gray-900' : 'bg-gray-50 dark:bg-gray-900'} ${cellMembers.length > 0 && !selfInCell ? 'uk-box-shadow-small' : ''} - hover:bg-gray-100`; + hover:bg-gray-100 dark:hover:bg-gray-800`; cellTd.addEventListener('click', () => handleClick(rowIndex, colIndex)); cellTd.textContent = value; @@ -118,7 +118,7 @@ async function buildSpreadsheet(otherMembers: Member[], self: Member) { sheetRow.className = 'uk-table-middle'; const sheetTd = document.createElement('td'); - sheetTd.className = 'uk-text-bold uk-text-center uk-background-muted border border-gray-300 p-2'; + sheetTd.className = 'uk-text-bold uk-text-center uk-background-muted border border-gray-300 dark:border-gray-700 p-2'; sheetTd.textContent = (rowIndex + 1).toString(); sheetRow.appendChild(sheetTd); diff --git a/examples/spaces-member-location/react/src/components/Cell.tsx b/examples/spaces-member-location/react/src/components/Cell.tsx index 10f7a39f44..8e4f2412cb 100644 --- a/examples/spaces-member-location/react/src/components/Cell.tsx +++ b/examples/spaces-member-location/react/src/components/Cell.tsx @@ -53,10 +53,10 @@ function Cell({ "--member-color": self?.profileData.memberColor, "--cell-member-color": cellMembers[0]?.profileData.memberColor, } as React.CSSProperties} - className={`uk-table-middle border border-gray-300 p-2 cursor-pointer transition-colors relative - ${selfInCell ? 'bg-white' : 'bg-gray-50'} + className={`uk-table-middle border border-gray-300 dark:border-gray-700 p-2 cursor-pointer transition-colors relative + ${selfInCell ? 'bg-white dark:bg-gray-900' : 'bg-gray-50 dark:bg-gray-900'} ${cellMembers.length > 0 && !selfInCell ? 'uk-box-shadow-small' : ''} - hover:bg-gray-100`} + hover:bg-gray-100 dark:hover:bg-gray-800`} onClick={() => handleClick(rowIndex, colIndex)} data-name-content={memberName ? cellLabel : ""} > diff --git a/examples/spaces-member-location/react/src/components/Spreadsheet.tsx b/examples/spaces-member-location/react/src/components/Spreadsheet.tsx index 713a510168..b5ef390d2b 100644 --- a/examples/spaces-member-location/react/src/components/Spreadsheet.tsx +++ b/examples/spaces-member-location/react/src/components/Spreadsheet.tsx @@ -32,11 +32,11 @@ const Spreadsheet = ({ }; return ( - + {cellData.map((row: string[], rowIndex: number) => ( - + {rowIndex + 1} {row.map((col, colIndex) => { diff --git a/examples/spaces-member-location/react/src/styles/styles.css b/examples/spaces-member-location/react/src/styles/styles.css index 9dd5977c56..c59ac476cf 100644 --- a/examples/spaces-member-location/react/src/styles/styles.css +++ b/examples/spaces-member-location/react/src/styles/styles.css @@ -69,6 +69,10 @@ height: 100vh; } +.dark .container { + background-color: hsl(var(--background)); +} + input { padding: 0.5rem; width: 100%; diff --git a/src/components/CodeEditor/CodeEditor.css b/src/components/CodeEditor/CodeEditor.css index e4ebedd567..b6e80800be 100644 --- a/src/components/CodeEditor/CodeEditor.css +++ b/src/components/CodeEditor/CodeEditor.css @@ -5,4 +5,11 @@ div.remove-border-radius { border-radius: 0; +} + +/* Sandpack renders the preview on a fixed white "browser" surface. In dark mode + that shows as a white panel around/behind the themed example, so flip it to + the dark canvas (neutral-1300, the inverse of white). */ +.ui-theme-dark .sp-preview-container { + background-color: var(--color-neutral-1300); } \ No newline at end of file diff --git a/src/components/Examples/ExamplesRenderer.tsx b/src/components/Examples/ExamplesRenderer.tsx index 33a4a2c135..586d670d35 100644 --- a/src/components/Examples/ExamplesRenderer.tsx +++ b/src/components/Examples/ExamplesRenderer.tsx @@ -10,6 +10,7 @@ import Icon from 'src/components/Icon'; import { IconName } from 'src/components/Icon/types'; import SegmentedControl from 'src/components/ui/SegmentedControl'; import dotGrid from './images/dot-grid.svg'; +import { withThemeSync } from './sandpackThemeSync'; import cn from 'src/utilities/cn'; import { getRandomChannelName } from '../../utilities/get-random-channel-name'; // Shared tsconfig for proper ES2020+ transpilation in Sandpack @@ -97,6 +98,15 @@ const ExamplesRenderer = ({ }, [files, apiKey]); const languageFiles = rewrittenFiles[activeLanguage]; + // Bake the current theme into the example files. Combined with the keyed + // SandpackProvider below, a theme change fully reloads the preview into the + // matching theme — a full reload (not an in-place hot reload) so the example's + // side effects, e.g. its Ably connection, are torn down rather than stacking + // up a fresh instance on each toggle. + const themedFiles = useMemo( + () => withThemeSync(languageFiles, activeLanguage, resolvedTheme), + [languageFiles, activeLanguage, resolvedTheme], + ); const isVerticalLayout = useMemo(() => layout === 'single-vertical' || layout === 'double-vertical', [layout]); const isDoubleLayout = useMemo(() => layout === 'double-horizontal' || layout === 'double-vertical', [layout]); @@ -105,8 +115,13 @@ const ExamplesRenderer = ({ return ( `// Injected by the docs site — applies the docs theme to this preview. +(function () { + if (typeof window === 'undefined') return; + // Class-based dark mode so Tailwind \`dark:\` utilities follow the class rather + // than the OS preference (the Play CDN defaults to media). + window.tailwind = window.tailwind || {}; + window.tailwind.config = Object.assign({}, window.tailwind.config, { darkMode: 'class' }); + + document.documentElement.classList.toggle('dark', ${isDark}); + document.documentElement.style.colorScheme = ${isDark ? "'dark'" : "'light'"}; + + // The examples' --primary token is identical in light and dark, so + // uk-text-primary (used for body text) would stay a low-contrast mid-slate on + // dark. Remap it to the foreground token in dark; scoped to .dark so light is + // untouched. + var fixes = document.createElement('style'); + fixes.textContent = '.dark .uk-text-primary{color:hsl(var(--foreground))}'; + (document.head || document.documentElement).appendChild(fixes); +})(); +`; + +// Entry module per template (see data/createPages: react files are stripped of +// their `src/` prefix, javascript files keep their project-relative path). Match +// on a pattern so both `index.tsx` and `main.tsx` entries (and any `src/`-prefixed +// variant) are found. +const ENTRY_PATTERN_BY_LANGUAGE: Partial> = { + react: /(^|\/)(index|main)\.tsx$/, + javascript: /(^|\/)script\.ts$/, +}; + +// Sandpack accepts either a raw code string or a { code, hidden } object per +// file (the renderer already relies on this for the injected tsconfig). +type SandpackFilesMap = Record; + +const codeOf = (file: string | { code?: string }): string => (typeof file === 'string' ? file : (file.code ?? '')); + +/** + * Return a copy of an example's files with the theme baked in: a hidden module + * (imported from the entry) applies the current theme on load, and the HTML head + * gets the class-based dark-mode config ahead of the Tailwind CDN. Regenerated + * per theme, so the preview reloads to match when the docs theme changes. + */ +export const withThemeSync = ( + files: ExampleFiles[LanguageKey], + language: LanguageKey, + resolvedTheme: 'light' | 'dark', +): SandpackFilesMap | undefined => { + if (!files) { + return files; + } + + const isDark = resolvedTheme === 'dark'; + const result = { ...files } as SandpackFilesMap; + + // Add the theme module in the entry's directory and import it, so a relative + // `./` import resolves in both templates (react's entry is at the root; the + // javascript entry lives under src/). + const entryPattern = ENTRY_PATTERN_BY_LANGUAGE[language]; + const entryKey = entryPattern ? Object.keys(result).find((key) => entryPattern.test(key)) : undefined; + if (entryKey) { + const slash = entryKey.lastIndexOf('/'); + const entryDir = slash >= 0 ? entryKey.slice(0, slash + 1) : ''; + result[`${entryDir}${THEME_SYNC_FILE}`] = { code: themeSyncModule(isDark), hidden: true }; + + const importLine = `import './${THEME_SYNC_FILE}';\n`; + const original = result[entryKey]; + const code = codeOf(original); + if (!code.includes(THEME_SYNC_FILE)) { + result[entryKey] = + typeof original === 'string' ? importLine + original : { ...(original as object), code: importLine + code }; + } + } + + // Set up the theme in the HTML , ahead of the Tailwind Play CDN and the + // bundle, so `dark:` utilities key off the class and — crucially — the page + // paints on the dark canvas from the very first frame. Without this the iframe + // shows a white flash before the JS entry runs, which is visible on the slower + // re-bundle that a language switch triggers. + const htmlKey = + 'index.html' in result ? 'index.html' : Object.keys(result).find((key) => key.replace(/^\//, '') === 'index.html'); + if (htmlKey) { + const html = codeOf(result[htmlKey]); + if (!html.includes('darkMode')) { + const headTag = + `` + + `${isDark ? '' : ''}`; + const injected = html.includes('') + ? html.replace('', `\n ${headTag}`) + : `${headTag}\n${html}`; + const originalHtml = result[htmlKey]; + result[htmlKey] = typeof originalHtml === 'string' ? injected : { ...(originalHtml as object), code: injected }; + } + } + + return result; +}; diff --git a/src/templates/examples.tsx b/src/templates/examples.tsx index bcb678c929..0900bd249c 100644 --- a/src/templates/examples.tsx +++ b/src/templates/examples.tsx @@ -61,19 +61,27 @@ const MarkdownOverrides = { ), }, thead: { - component: ({ children }: PropsWithChildren) => {children}, + component: ({ children }: PropsWithChildren) => ( + {children} + ), }, tbody: { - component: ({ children }: PropsWithChildren) => {children}, + component: ({ children }: PropsWithChildren) => ( + + {children} + + ), }, tr: { component: ({ children }: PropsWithChildren) => ( - {children} + + {children} + ), }, th: { component: ({ children }: PropsWithChildren) => ( - + {children} ),
Simulate 4 betting odds for a sports game before attaching.
2.50
3.42
2.87
{matchData.match.matchOdds.homeWin}
{matchData.match.matchOdds.draw}
{matchData.match.matchOdds.awayWin} @@ -133,12 +133,12 @@ function LiveMatch({ channelName }: { channelName: string }) {
Move your cursor over the screen to see live cursors in action
Move your cursor over the screen to see live cursors in action diff --git a/examples/spaces-live-cursors/react/src/styles/styles.css b/examples/spaces-live-cursors/react/src/styles/styles.css index 9dd5977c56..c59ac476cf 100644 --- a/examples/spaces-live-cursors/react/src/styles/styles.css +++ b/examples/spaces-live-cursors/react/src/styles/styles.css @@ -69,6 +69,10 @@ height: 100vh; } +.dark .container { + background-color: hsl(var(--background)); +} + input { padding: 0.5rem; width: 100%; diff --git a/examples/spaces-member-location/javascript/index.html b/examples/spaces-member-location/javascript/index.html index 3b6fd354f9..4dac473654 100644 --- a/examples/spaces-member-location/javascript/index.html +++ b/examples/spaces-member-location/javascript/index.html @@ -9,7 +9,7 @@