When displaying certain types of terminal content, such as [[Figlet - ASCII Big Text|ASCII art]], I wanted to really give it the vibe of a retro computer in my [[Obsidian Editor]] notes. I was looking for something like this old amber monochrome portable computer I inherited. Nostalgia doesn't care about accuracy, and so while the display on that computer was actually an early LCD monitor, a CRT better fits with the retro aesthetic. I was inspired by both early LCDs and old school electron gun CRT displays when researching and assembling these CSS snippets. ![[Screenshot from 2023-09-11 20-27-17.png]] # The Snippets I broke this down into several smaller snippets that can be activated on demand using the `cssclasses` frontmatter/property key. ## Frontmatter/Property Example The list of CSS classes matches with the filenames of the snippets. ```yaml --- cssclasses: code-nowrap, code-amber, code-glow, read-code-screen, read-code-crt --- ``` I don't usually combine both `read-code-screen` and `read-code-crt` because to me they represent different looks, but they function together just fine. I have a particular way of organizing my snippets so I can tell at a glance what they affect and in what situation. In particular the `read` prefix indicates that it only applied to the "Reading View" in Obsidian, and not the live preview or source mode. ## Snippet: Make Code Blocks Glow File: `code-glow.css` Gives the text inside code blocks a little bit of glow so that they have that hazy look of old displays. ![[Screenshot from 2023-09-11 20-25-04.png]] ```css .markdown-source-view.mod-cm6.code-glow .cm-line.HyperMD-codeblock .cm-hmd-codeblock, .markdown-reading-view .code-glow pre code { text-shadow: 0 0 5px; -webkit-font-smoothing: auto; } ``` ## Snippet: Make Code Block Text Amber File: `code-amber.css` One of the common types of old CRT and early LCD displays was known as "amber monochrome". In order to choose what color to use here I went down a deep rabbit hole on what [[CRT Phosphor Wavelengths|the actual color]] of these displays might have been. ![[Screenshot from 2023-09-11 20-24-28.png]] ```css :root { --color-monospace: #ffa900; } /* We only want to match elements that have just this class, and not match if it also has any other classes. This allows the syntax highlighting of code blocks to work properly for known types. The "where" is a lower priority selector, which allows us to do that. However, it still sets the default color to amber, so anything not specifically tagged by the syntax highlighter will still be amber instead of whatever your default color is. Since there is no difference in the selectors at any level, the only option may be to add some Javascript which checks the contents of the `.code-block-flair` `span` in the the first consecutive `.HyperMD-codeblock` `div` in the group, and only applies amber to the subsequent blocks which do not have an `svg`. There might be a convoluted way to do this in pure CSS using sibling and not selectors, so I might give that a shot later. */ :where(.code-amber [class="cm-hmd-codeblock"]), .markdown-reading-view .code-amber pre code { color: var(--color-monospace); } ``` ## Snippet: Reading View Screen Texture File: `read-code-screen.css` Particularly on those early LCDs you could really see the pixels and the screen door effect. This snippet adds a texture to code blocks to give it that low res feel without reducing legibility. ![[Screenshot from 2023-09-11 20-25-43.png]] >[!NOTE] Credit to Alec Lownes > Among the dozen or more different retro styled CSS tutorials I found, Alec's gave the best result and [this is the one](http://aleclownes.com/2017/02/01/crt-display.html) that I modified very slightly to work with Obsidian. Alec says that the intention was to match how old CRTs looked, but it looks more like LCD than CRT to me. Either way, it's definitely aesthetic. ```css .markdown-reading-view .read-code-screen pre::before { content: " "; display: block; position: absolute; top: 0; left: 0; bottom: 0; right: 0; background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06)); z-index: 2; background-size: 100% 2px, 3px 100%; pointer-events: none; } ``` ## Snippet: Reading View CRT Flicker File: `read-code-crt.css` Vintage or damaged CRTs often show artifacts in their output which cause the text to wobble, smear, and flicker. When they were new this wasn't as obvious, but over time this has become the iconic look of CRTs that most people are familiar with. This snippet is animated, despite the static image below. ![[Screenshot from 2023-09-11 20-26-11.png]] >[!NOTE] Credit to Alec Lownes > Among the dozen or more different CRT flickering CSS tutorials I found, Alec's gave the best result and [this is the one](http://aleclownes.com/2017/02/01/crt-display.html) that I repurposed to work in Obsidian. ```css @keyframes crtFlicker { 0% { opacity: 0.27861; } 5% { opacity: 0.34769; } 10% { opacity: 0.23604; } 15% { opacity: 0.90626; } 20% { opacity: 0.18128; } 25% { opacity: 0.83891; } 30% { opacity: 0.65583; } 35% { opacity: 0.67807; } 40% { opacity: 0.26559; } 45% { opacity: 0.84693; } 50% { opacity: 0.96019; } 55% { opacity: 0.08594; } 60% { opacity: 0.20313; } 65% { opacity: 0.71988; } 70% { opacity: 0.53455; } 75% { opacity: 0.37288; } 80% { opacity: 0.71428; } 85% { opacity: 0.70419; } 90% { opacity: 0.7003; } 95% { opacity: 0.36108; } 100% { opacity: 0.24387; } } @keyframes crtTextShadow { 0% { text-shadow: 0.4389924193300864px 0 1px rgba(0, 30, 255, 0.5), -0.4389924193300864px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } 5% { text-shadow: 2.7928974010788217px 0 1px rgba(0, 30, 255, 0.5), -2.7928974010788217px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } 10% { text-shadow: 0.02956275843481219px 0 1px rgba(0, 30, 255, 0.5), -0.02956275843481219px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } 15% { text-shadow: 0.40218538552878136px 0 1px rgba(0, 30, 255, 0.5), -0.40218538552878136px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } 20% { text-shadow: 3.4794037899852017px 0 1px rgba(0, 30, 255, 0.5), -3.4794037899852017px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } 25% { text-shadow: 1.6125630401149584px 0 1px rgba(0, 30, 255, 0.5), -1.6125630401149584px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } 30% { text-shadow: 0.7015590085143956px 0 1px rgba(0, 30, 255, 0.5), -0.7015590085143956px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } 35% { text-shadow: 3.896914047650351px 0 1px rgba(0, 30, 255, 0.5), -3.896914047650351px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } 40% { text-shadow: 3.870905614848819px 0 1px rgba(0, 30, 255, 0.5), -3.870905614848819px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } 45% { text-shadow: 2.231056963361899px 0 1px rgba(0, 30, 255, 0.5), -2.231056963361899px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } 50% { text-shadow: 0.08084290417898504px 0 1px rgba(0, 30, 255, 0.5), -0.08084290417898504px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } 55% { text-shadow: 2.3758461067427543px 0 1px rgba(0, 30, 255, 0.5), -2.3758461067427543px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } 60% { text-shadow: 2.202193051050636px 0 1px rgba(0, 30, 255, 0.5), -2.202193051050636px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } 65% { text-shadow: 2.8638780614874975px 0 1px rgba(0, 30, 255, 0.5), -2.8638780614874975px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } 70% { text-shadow: 0.48874025155497314px 0 1px rgba(0, 30, 255, 0.5), -0.48874025155497314px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } 75% { text-shadow: 1.8948491305757957px 0 1px rgba(0, 30, 255, 0.5), -1.8948491305757957px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } 80% { text-shadow: 0.0833037308038857px 0 1px rgba(0, 30, 255, 0.5), -0.0833037308038857px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } 85% { text-shadow: 0.09769827255241735px 0 1px rgba(0, 30, 255, 0.5), -0.09769827255241735px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } 90% { text-shadow: 3.443339761481782px 0 1px rgba(0, 30, 255, 0.5), -3.443339761481782px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } 95% { text-shadow: 2.1841838852799786px 0 1px rgba(0, 30, 255, 0.5), -2.1841838852799786px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } 100% { text-shadow: 2.6208764473832513px 0 1px rgba(0, 30, 255, 0.5), -2.6208764473832513px 0 1px rgba(255, 0, 80, 0.3), 0 0 3px; } } .markdown-reading-view .read-code-crt pre code:before { content: " "; display: block; position: absolute; top: 0; left: 0; bottom: 0; right: 0; background: linear-gradient( to bottom, rgba(0, 0, 0, 0) 70%, rgba(40, 40, 30, 0.4) 30%); background-size: 100% 4px; z-index: 2; pointer-events: none; } .markdown-reading-view .read-code-crt pre code { animation: crtTextShadow 1.6s infinite; } .markdown-reading-view .read-code-crt pre code::after { content: " "; display: block; position: absolute; top: 0; left: 0; bottom: 0; right: 0; background: rgba(18, 16, 16, 0.1); opacity: 0; z-index: 2; pointer-events: none; animation: crtFlicker 0.15s infinite; } ``` ## Snippet: Don't Wrap Code Blocks File: `code-nowrap.css` By default, when there is too much text to fit on a single line Obsidian will "wrap" it around to the next. This is typically what you want, but sometimes it ruins ASCII art. This snippet disables that for code blocks. >[!WARNING] > I noticed that this snippet sometimes doesn't scroll the background of the other snippets, so they might not look ideal all combined on a narrow screen, but I generally find this to be good enough. ```css .code-nowrap pre code, .code-nowrap .cm-hmd-codeblock { word-wrap: break-word; white-space: pre; overflow-x: auto; } .code-nowrap .cm-hmd-codeblock:first-of-type { margin-left: max(calc(50% + var(--folding-offset) - var(--line-width-adaptive)/ 2), calc(50% + var(--folding-offset) - var(--max-width)/ 2))!important; } .markdown-source-view.mod-cm6.is-readable-line-width.code-nowrap div.cm-line.HyperMD-codeblock-bg { min-width: calc(var(--line-width-adaptive) - var(--folding-offset)); width: 100% !important; max-width: 100% !important; margin-left: 0 !important; margin-right: 0 !important; } ``` # See Also - [[CRT Phosphor Wavelengths]] - [[Wavelength to RGB Conversion]] - [[CRT Snippet Test]] # References - My primary source was [this post](http://aleclownes.com/2017/02/01/crt-display.html) by [Alec Lownes](http://aleclownes.com/) which I modified slightly to work with [[Obsidian Editor]]'s snippets - https://blog.webguy.pw/blog/simulating-a-crt-monitor-in-css/ - https://css-tricks.com/old-timey-terminal-styling/ - https://dev.to/ekeijl/retro-crt-terminal-screen-in-css-js-4afh