Terminal Styling#
Dokploy uses xterm.js (@xterm/xterm) for all interactive terminal components. Because xterm.js renders into a shadow-DOM-like canvas/DOM structure, its theming is split between xterm's own theme option (passed at construction) and CSS rules in globals.css that target xterm's generated class names. This split makes terminal styling sensitive to any global CSS change β most notably, major UI framework upgrades.
Terminal Components#
Two terminal component implementations exist in the codebase:
| Component | Path | Use Case |
|---|---|---|
Terminal | settings/web-server/terminal.tsx | SSH into web servers (local + remote) |
DockerTerminal | docker/terminal/docker-terminal.tsx | Exec into Docker containers (bash / sh) |
Both follow the same pattern: xterm is initialized inside a useEffect, configured with identical theme options, and connected to a WebSocket backend via AttachAddon.
XTerm Theme Configuration#
Both terminals are constructed with :
theme: {
cursor: resolvedTheme === "light" ? "#000000" : "transparent",
background: "rgba(0, 0, 0, 0)",
foreground: "currentColor",
}
Key decisions:
background: "rgba(0, 0, 0, 0)"β transparent, so the terminal inherits the surrounding card/panel background. Avoids a hard-coded black box in light mode.foreground: "currentColor"β inherits the CSScolorvalue from the wrapping element, making text automatically adapt to light/dark mode.cursorβ explicitly#000000in light mode (xterm's default cursor is white, invisible on a light background);transparentin dark mode. This was introduced to fix the light-mode invisible-cursor regression in PR #936.
resolvedTheme is provided by the next-themes hook useTheme() .
CSS Cascade Dependencies (globals.css)#
Three xterm-specific CSS sections in apps/dokploy/styles/globals.css complete the styling:
1. Viewport geometry :
.xterm-viewportβ addsborder-radius: 0.75remto round the terminal's scroll container..xterm .xterm-viewportβ forcesoverflow-y: autoto ensure the terminal is scrollable..xterm .xterm-screenβ setsoverflow: hiddento clip canvas overflow.
2. Paste highlight :
.xterm-bg-257.xterm-fg-257 {
background-color: var(--terminal-paste) !important;
color: currentColor !important;
}
xterm uses color index 257 for clipboard paste selections. Without this rule, pasted text appears invisible. The --terminal-paste CSS variable is defined per theme :
- Light mode:
rgba(0, 0, 0, 0.2) - Dark mode:
rgba(255, 255, 255, 0.2)
3. Monospace font enforcement β an earlier regression (#terminal span being overridden by global Inter sans-serif styles) was fixed in PR #268 by removing the offending #terminal span global rule and explicitly setting fontFamily to a full monospace stack in the xterm config. The current components carry lineHeight: 1.4 and convertEol: true .
Recurring Regression Pattern#
Terminal styling has regressed repeatedly at framework boundaries. The root cause is the same each time: xterm-generated class names (xterm-*) are invisible to Tailwind's purge/scan, so they must be maintained as explicit CSS rules β any global CSS reset or @layer base change can silently break them.
| PR | Regression | Root Cause | Fix |
|---|---|---|---|
| #268 | Monospace font lost | Global #terminal span rule injected Inter + letter-spacing | Removed global rule; added fontFamily to xterm config |
| #936 | Text invisible in light mode | foreground/cursor not theme-aware | Added useTheme(), set foreground: "currentColor", cursor: "#000000" for light |
| #1203 | Pasted text invisible | Missing ClipboardAddon + no paste-highlight CSS | Loaded ClipboardAddon; added --terminal-paste variable and .xterm-bg-257 rule |
| #4706 | Tailwind v4 migration | 159-file rewrite of globals.css and all utility classes | Preserved xterm-* rules in @layer utilities; migrated syntax |
Adding a New Terminal Component#
When creating a new terminal component:
- Copy the
themeobject pattern from either existing component β do not hard-code colors. - Import
@xterm/xterm/css/xterm.cssβ required for xterm's internal layout. - Load
ClipboardAddonif paste support is needed . - Do not add CSS rules that target
#terminal spanor other xterm internals β use the xtermthemeoption instead. - Verify in both light and dark mode before merging.
Key Files#
| File | Purpose |
|---|---|
settings/web-server/terminal.tsx | SSH terminal component |
docker/terminal/docker-terminal.tsx | Docker exec terminal component |
styles/globals.css | xterm viewport/paste/font CSS rules |