Ludicord
Guide 12 · Discord

Responsive Activities

Adapt to Discord layout mode, orientation, safe areas, locale, and thermal state.

Ludicord 3.1.0Public source 20c4889

An Activity can appear full screen, beside chat, on a mobile device, or in a compact Discord layout. Build from the available container and Discord context instead of assuming a normal website viewport.

#Start with safe-area CSS

Ludicord installs CSS variables from Discord's safe-area data:

csscss
1/* file: app/globals.css */
2.controls {
3 position: fixed;
4 right: calc(1rem + var(--ludicord-safe-right));
5 bottom: calc(1rem + var(--ludicord-safe-bottom));
6}

Use safe areas on edges that can overlap Discord controls. Do not add the inset to every nested component, or padding will accumulate.

#Read numeric safe-area values

tsxtsx
1import { useLudicordSafeArea } from "ludicord/discord";
2
3const safeArea = useLudicordSafeArea();
4canvas.resize({ top: safeArea.top, bottom: safeArea.bottom });

Prefer CSS for ordinary layout. Use the hook for canvases, physics viewports, or libraries that require numbers.

#React to Discord layout signals

tsxtsx
1const layoutMode = useActivityLayoutMode();
2const orientation = useOrientation();
3const thermalState = useThermalState();
4const locale = useDiscordLocale();
SignalUseful for
Layout modeChanging interaction model when Discord switches presentation
OrientationRepositioning controls or a game board
Thermal stateReducing effects or simulation work on constrained devices
LocaleFormatting and selecting translated UI

Values can be null outside Discord or before the event arrives. Build a reasonable CSS default first.

#Combine CSS and runtime data

Use media/container queries for width and runtime hooks for behavior that CSS cannot express.

components/game-board.tsxtsx
1const thermal = useThermalState();
2const effects = thermal === "critical" ? "low" : "high";
3
4return <GameCanvas effects={effects} />;

#Mobile interaction checklist

  • Keep touch targets at least 44px in both dimensions.
  • Avoid hover-only information and actions.
  • Test the smallest supported Activity width.
  • Let text wrap and grow at 200% browser text size.
  • Put essential controls above the bottom safe area.
  • Pause expensive animation when useActivityLifecycle().visible is false.
  • Respect prefers-reduced-motion.
  • Do not require a hardware keyboard.

#Minimized UI

app/minimize.tsx is connected automatically. Keep it lightweight and focused on the one fact or action useful while the full Activity is hidden.

app/minimize.tsxtsx
1import { Minimize } from "ludicord";
2
3export default function MinimizedActivity() {
4 return <Minimize heading="Game night" message="Your round is still running." />;
5}

The minimized surface should not duplicate the full app or create a second navigation system.

#Next step

Move trusted work to API routes, the first server-side convention in the learning path.