Skip to content

Commit bc02e99

Browse files
authored
feat(css): bundle CSS with JS and support themes (#24)
* feat(styling): bundle css with js * fix(styling): add css variables with fallback * chore(docs): add interactive styling examples * chore(docs): remove css section from the docs * chore(docs): add docs on custom styling
1 parent 6f992d8 commit bc02e99

File tree

13 files changed

+263
-76
lines changed

13 files changed

+263
-76
lines changed

CONTRIBUTING.md

+7
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ Contributing your first bit of code is simple:
3131
5. Push changes to your fork
3232
6. Open a pull request against `master` branch
3333

34+
#### Make CSS changes
35+
36+
We use a very simple CSS build system to ship raw CSS with the bundle.
37+
If you want to make visual changes to how the terminal looks, edit `src/ttty.css` and then run `npm run build:css` to apply your changes.
38+
39+
If you want to edit the preview website look, head over to `preview/index.css` (no extra steps are nessesary). Some critical CSS can also be found in `index.html`, inlined for better FCP.
40+
3441
#### Styleguides
3542

3643
We use [standard js](https://standardjs.com/) (with minor tweaks) as our style set.

README.md

+14-5
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,6 @@ const terminal = initTerminal({ /* settings */ })
4343
ttty.initTerminal({ /* settings */ })
4444
```
4545

46-
Don't forget to include / import the required css:
47-
```html
48-
<link rel="stylesheet" href="https://unpkg.com/ttty/dist/ttty.css">
49-
```
50-
5146
Initialize with parameters:
5247

5348
```js
@@ -144,6 +139,20 @@ const term = document.getElementById('terminal');
144139
term.addEventListener('onCommand', e => console.log("known command executed!"));
145140
```
146141

142+
## Custom styling
143+
144+
You can customize the look defining custom CSS variables. If you have multiple instances, you can even have each instance in its' own style!
145+
146+
| Variable | Description
147+
| ------------- | ------------- |
148+
| `--terminal-bg-color` | Background color |
149+
| `--terminal-fg-color` | Text color |
150+
| `--terminal-font` | Terminal font-family |
151+
| `--terminal-accent-color` | Accent color |
152+
| `--terminal-error-color` | Error color |
153+
147154
## Browser compatibility
148155

149156
ttty is built and distributed with ES6 in mind (including the minified package). You can always transpile & bundle it targeting your browser set of choice.
157+
158+
Browsers that do not support CSS variables (IE < 11) might not be able to make use of custom themes. In order to use ttty with older browsers please rebuild this with custom properties removed.

index.html

+17-5
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,26 @@
77
<meta name="description" content="A lightweight (1kb) browser terminal powered by TypeScript" />
88
<meta name="theme-color" content="#000000">
99
<link rel="icon" href="favicon.svg">
10-
<link rel="stylesheet" href="/ttty.css">
10+
<style>
11+
body {
12+
height: 100vh;
13+
margin: 0;
14+
display: flex;
15+
}
16+
.terminal, aside {
17+
width: 500px;
18+
flex-shrink: 0;
19+
background-color: black;
20+
}
21+
</style>
1122
</head>
1223
<body>
1324
<aside id="terminal">
1425
</aside>
1526
<main>
1627
<h1>ttty</h1>
1728
<hr/>
18-
<h3>A tiny browser terminal "emulator"</h3>
29+
<h2>A tiny browser terminal "emulator"</h2>
1930
<article>
2031
<p>
2132
Unlike <a href="https://github.com/vercel/hyper" target="_blank" rel="noreferrer noopener">hyper</a>
@@ -25,15 +36,16 @@ <h3>A tiny browser terminal "emulator"</h3>
2536
<p>
2637
Available on NPM or as a "drop-in" ES module / js file.
2738
</p>
28-
<h4>
39+
<h3>
2940
Featuring:
30-
</h4>
41+
</h3>
3142
<ul>
3243
<li>Tiny, dependency-free and built with modern JS</li>
3344
<li>Easy-to-add custom commands</li>
45+
<li data-exec="color random">Customizable look</li>
3446
<li>Events</li>
3547
<li>Command history</li>
36-
<li data-exec="help">A help message</li>
48+
<li data-exec="help">A built-in help message</li>
3749
<li data-exec="multiply">Command arguments with validation</li>
3850
<li data-exec="start">Interruptable "foreground process" imitation</li>
3951
<li>Small but powerful API</li>

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@
1818
"sideEffects": false,
1919
"scripts": {
2020
"start": "vite",
21-
"build": "tsc && vite build --config lib.config.js && npm run emit && rm dist/favicon.ico",
22-
"build:site": "tsc && vite build --config site.config.js",
21+
"build": "npm run build:css && tsc && vite build --config lib.config.js && npm run emit && rm dist/favicon.ico",
22+
"build:site": "npm run build:css && tsc && vite build --config site.config.js",
2323
"emit": "tsc --declaration --module esnext --moduleResolution node --target esnext --outDir dist src/index.ts",
24+
"build:css": "node scripts/buildCss.js",
2425
"typecheck": "tsc --noEmit --incremental false",
2526
"lint": "eslint \"{src,preview}/**/*.{ts,tsx}\""
2627
},

preview/colors.ts

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
export const COLOR_SCHEMES = ['default', 'solarized', 'light', 'ubuntu', 'classic', 'borland', 'gotham', 'horizon', 'icorange', 'lavandula', 'lunaria', 'amber']
2+
3+
const COLOR_MAP: Record<string, Record<string, string>> = {
4+
default: {
5+
'--terminal-bg-color': 'black',
6+
'--terminal-fg-color': 'white',
7+
'--terminal-accent-color': '#ffff7d',
8+
'--terminal-error-color': '#cc1010'
9+
},
10+
solarized: {
11+
'--terminal-bg-color': '#073642',
12+
'--terminal-fg-color': '#fdf6e3',
13+
'--terminal-accent-color': '#b58900',
14+
'--terminal-error-color': '#dc322f'
15+
},
16+
light: {
17+
'--terminal-bg-color': 'white',
18+
'--terminal-fg-color': 'black',
19+
'--terminal-accent-color': '#b58900',
20+
'--terminal-error-color': '#9f211f'
21+
},
22+
ubuntu: {
23+
'--terminal-bg-color': '#300B23',
24+
'--terminal-fg-color': 'white',
25+
'--terminal-accent-color': '#89DE49',
26+
'--terminal-error-color': '#E0232C'
27+
},
28+
borland: {
29+
'--terminal-bg-color': '#0015a0',
30+
'--terminal-fg-color': '#ffff4e',
31+
'--terminal-accent-color': '#a8ff60',
32+
'--terminal-error-color': '#ff6c60'
33+
},
34+
gotham: {
35+
'--terminal-bg-color': '#0a0f14',
36+
'--terminal-fg-color': '#98d1ce',
37+
'--terminal-accent-color': '#26a98b',
38+
'--terminal-error-color': '#c33027'
39+
},
40+
horizon: {
41+
'--terminal-bg-color': '#fdf0ed',
42+
'--terminal-fg-color': '#1c1e26',
43+
'--terminal-accent-color': '#1eb980',
44+
'--terminal-error-color': '#da103f'
45+
},
46+
icorange: {
47+
'--terminal-bg-color': '#262626',
48+
'--terminal-fg-color': '#ffcb83',
49+
'--terminal-accent-color': '#a4a900',
50+
'--terminal-error-color': '#c13900'
51+
},
52+
lavandula: {
53+
'--terminal-bg-color': '#050014',
54+
'--terminal-fg-color': '#736e7d',
55+
'--terminal-accent-color': '#337e6f',
56+
'--terminal-error-color': '#7d1625'
57+
},
58+
lunaria: {
59+
'--terminal-bg-color': '#ebe4e1',
60+
'--terminal-fg-color': '#484646',
61+
'--terminal-accent-color': '#497d46',
62+
'--terminal-error-color': '#783c1f'
63+
},
64+
amber: {
65+
'--terminal-bg-color': '#2b1900',
66+
'--terminal-fg-color': '#ff9400',
67+
'--terminal-accent-color': '#ff9400',
68+
'--terminal-error-color': '#ff9400'
69+
},
70+
classic: {
71+
'--terminal-bg-color': 'black',
72+
'--terminal-fg-color': '#87DE47',
73+
'--terminal-accent-color': '#87DE47',
74+
'--terminal-error-color': '#ED262E'
75+
}
76+
}
77+
78+
let currentScheme = 'default'
79+
80+
export const setColor = (color: string) => {
81+
if (COLOR_SCHEMES.includes(color) || color === 'random') {
82+
const colorSchemes = COLOR_SCHEMES.filter(c => c !== currentScheme)
83+
const endColor = color === 'random' ? colorSchemes[Math.floor(Math.random() * colorSchemes.length)] : color
84+
const scheme = COLOR_MAP[endColor]
85+
Object.keys(scheme).forEach(key => {
86+
document.documentElement.style
87+
.setProperty(key, scheme[key])
88+
})
89+
currentScheme = endColor
90+
return `Color scheme set to ${endColor}`
91+
} else {
92+
return `Color scheme "${color}" was not found. <br/><br/>Available schemes: ${[...COLOR_SCHEMES, 'random'].join(', ')}.`
93+
}
94+
}

preview/index.css

+9-11
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
1-
body {
2-
height: 100vh;
3-
margin: 0;
4-
display: flex;
5-
}
6-
.terminal, aside {
7-
width: 500px;
8-
flex-shrink: 0;
9-
background-color: black;
10-
}
111
main {
122
padding: 2rem;
133
font-family: "Helvetica", sans-serif;
144
max-width: 800px;
155
}
6+
7+
aside {
8+
box-shadow: 8px 0 20px 0 #00000021;
9+
}
10+
1611
h1,h3 { margin-top: 0 }
1712
hr {
1813
border: 0;
@@ -24,7 +19,7 @@ h1 {
2419
margin-bottom: 1rem;
2520
font-family: monospace;
2621
}
27-
h3 {
22+
h2 {
2823
color: #979797;
2924
font-size: 1.5rem;
3025
}
@@ -51,4 +46,7 @@ li[data-exec] {
5146
width: 100%;
5247
height: 30%;
5348
}
49+
aside {
50+
box-shadow: none;
51+
}
5452
}

preview/main.ts

+20-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import initTerminal from '../src/api/init'
22
import './index.css'
3+
import { setColor } from './colors'
34

45
const docArea = document.querySelector('main')
56

@@ -9,17 +10,17 @@ const terminal = initTerminal({
910
* Documentation: <a href="https://github.com/mkrl/ttty/blob/master/README.md" target="_blank">https://github.com/mkrl/ttty</a><br/><br/>`,
1011
prompt: 'ttty:~$ ',
1112
commands: {
13+
test: {
14+
name: 'test',
15+
description: 'a test command with no arguments that just prints some text',
16+
func: ({ type }) => { type('The quick brown fox jumps over the lazy dog', 20) }
17+
},
1218
echo: {
1319
name: 'echo',
14-
description: 'a test command with one echo arg',
20+
description: 'echo whatever you pass as an argument',
1521
argDescriptions: ['a string to be echoed in console'],
1622
func: ({ print }, argument) => { print(argument) }
1723
},
18-
test: {
19-
name: 'test',
20-
description: 'A test command with no args that just prints some text',
21-
func: ({ type }) => { type('foo') }
22-
},
2324
multiply: {
2425
name: 'multiply',
2526
description: 'multiply two numbers',
@@ -39,6 +40,19 @@ const terminal = initTerminal({
3940
})
4041
}
4142
},
43+
color: {
44+
name: 'color',
45+
description: 'set a terminal color scheme',
46+
argDescriptions: ['color scheme'],
47+
func: ({ print }, color) => {
48+
print(setColor(color))
49+
}
50+
},
51+
clear: {
52+
name: 'clear',
53+
description: 'clear the history',
54+
func: ({ commandContainer }) => { commandContainer.innerHTML = '' }
55+
},
4256
github: {
4357
name: 'github',
4458
description: 'gets a Github user\'s name and number of public repositories',

public/ttty.css

-47
This file was deleted.

scripts/buildCss.js

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
const { exec } = require('child_process')
2+
const { writeFileSync } = require('fs')
3+
4+
const INPUT_FILE = './src/ttty.css'
5+
const OUTPUT_FILE = './src/helpers/style.generated.ts'
6+
7+
const NOTICE =
8+
`/*
9+
THIS FILE WAS AUTOMATICALLY GENERATED
10+
Do not modify it directly. To make style changes please go to ${INPUT_FILE}
11+
*/`
12+
13+
const getFile = style =>
14+
`${NOTICE}
15+
const TERMINAL_STYLE =
16+
'${style}'
17+
18+
export default TERMINAL_STYLE
19+
`
20+
21+
// Minifies one single css file and compiles it into a style.generated.ts module
22+
exec(`./node_modules/.bin/esbuild ${INPUT_FILE} --minify`, (error, stdout, stderr) => {
23+
if (error) {
24+
console.log(`ERR: ${error.message}`)
25+
return
26+
}
27+
if (stderr) {
28+
console.log(`ERR: ${stderr}`)
29+
return
30+
}
31+
writeFileSync(OUTPUT_FILE, getFile(stdout.trim()))
32+
})

src/api/init.ts

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { startProcess, stopProcess } from './process'
77
import { attachKeyboardListener } from '../helpers/keyboard'
88
import { dispatchEvent, TerminalEvent } from '../helpers/events'
99
import createHelp from '../helpers/help'
10+
import loadStyle from '../helpers/loadStyle'
1011

1112
const initTerminal = ({
1213
host,
@@ -24,6 +25,7 @@ const initTerminal = ({
2425
enableHelp,
2526
commands
2627
}
28+
loadStyle()
2729
const { commandContainer, input, inputContainer } = buildTree(host, prompt)
2830

2931
const terminal: TerminalInstance = {

0 commit comments

Comments
 (0)