Background
On commercial TVs, there's a mode called Video Wall mode, where you can stretch a single desktop across multiple screens. Let's say I'm working with 4 screens, I'm trying to make a web page that appears like it's not stretched, which means on a normal single screen showing one desktop it will look like it's been compressed 4 times.
I've currently got a partial solution by creating a 4 desktop wide page, then using CSS trasform to scale the page by 1/4 horizontally.
Although images are blurry, that could be mitigated by excluding transformation on elements with images.
The issue
When the scaling transform is done, the fonts are pixelated as if they went through a bad (low quality) jpeg compression.
Below is my current code sample.
body {
font-family: Helvetica, Arial, sans-serif;
color: #333;
display: flex;
flex-flow: row;
white-space: nowrap;
overflow: hidden;
min-width: min-content;
animation: fadein 3s;
}
@keyframes fadein {
from {
opacity: 0
}
to {
opacity: 1
}
}
.screen {
display: grid;
grid: 50% auto / 50% auto;
}
.main {
grid-column: 1 / 2;
grid-row: 1 / 2;
background-size: 100% 100%;
background-repeat: no-repeat;
float: left;
}
.dynamic {
grid-column: 1 / 2;
grid-row: 2 / 3;
float: left;
display: flex;
flex-flow: row;
justify-content: space-between;
}
.list {
grid-column: 2 / 3;
grid-row: 1 / 3;
}
.dynamic .item {
position: relative;
height: 100%;
width: 49.8%;
}
.dynamic .item > div {
position: relative;
transform-origin: left;
}
.dynamic .item .name {
display: block;
}
.dynamic .item .header > * {
display: inline-flex;
}
.dynamic .item .values > * {
display: inline-flex;
}
@media (min-aspect-ratio: 16/9) {
body {
font-size: 4vh;
height: 100vh;
}
.screen {
width: calc(8000vh/(9*5));
height: 100vh;
}
.dynamic .text { font-size: 2vh }
.dynamic .name > * { font-size: 4vh }
.list { font-size: 2vh }
.list .title { font-size: 4vh }
}
@media (aspect-ratio: 16/9) {
body {
flex-direction: row;
width: calc(8000vh/9);
height: 100vh;
transform: scaleX(.25);
transform-origin: top left;
}
}
<div class="screen">
<div class="list">
<div class="section">
<div class="title text">Subsection 1</div>
<div class="item">
<div class="text">Text 1</div>
<div class="text">Text 2</div>
<div class="text">Text 3</div>
</div>
</div>
<div class="section">
<div class="title text">Subsection 2</div>
<div class="item">
<div class="text">Text 1</div>
<div class="text">Text 2</div>
<div class="text">Text 3</div>
</div>
</div>
</div>
<div class="main"></div>
<div class="dynamic">
<div class="item">
<div class="name"><span class="text">Subsection 1</span></div>
<div class="header">
<div class="text"><span>Header 1</span></div>
<div class="text"><span>Header 2</span></div>
<div class="text"><span>Header 3</span></div>
<div class="text"><span>Header 4</span></div>
</div>
<div class="values">
<div class="text"><span>Text 1</span></div>
<div class="text"><span>Text 2</span></div>
<div class="text"><span>Text 3</span></div>
<div class="text"><span>Text 4</span></div>
</div>
</div>
<div class="item">
<div class="name"><span class="text">Subsection 2</span></div>
<div class="header">
<div class="text"><span>Header 1</span></div>
<div class="text"><span>Header 2</span></div>
<div class="text"><span>Header 3</span></div>
<div class="text"><span>Header 4</span></div>
</div>
<div class="values">
<div class="text"><span>Text 1</span></div>
<div class="text"><span>Text 2</span></div>
<div class="text"><span>Text 3</span></div>
<div class="text"><span>Text 4</span></div>
</div>
</div>
</div>
</div>
What is causing this, and is there a way to work around it, using HTML/CSS?
Let's say you had a website that was made with only mobile in mind, and you wanted to make it good to go for desktop, you wouldn't use transform scale, right? Simply don't use transform for that. Use media-queries and adjust the actual size of the elements accordingly. Using scale like that will mess with the pixel values and cause this blurry effect.
It doesn't matter how big or small the media you're working with is, the principles of responsive design work the same. Use relative units instead of absolute ones and it's not that hard.
If you use rem
units for font-sizes, margins and paddings and etc, and unitless line-heights, they are relative to the root font-size (which is normally 16px by default), so by modifying the font-size on the html
element, they will all scale in proportion. For general layout structure, you can use percentages, or even better, use CSS Grid and fr
units.
Images will require a bit more work, as you should provably use different images for different sizes, but for everything else, this does it.
A demonstration:
body {
margin: 0;
font-family: sans-serif;
line-height: 1.5;
}
p {
margin-top: 0;
margin-bottom: .75rem;
font-size: 1rem;
line-height: 1.5;
}
.main {
display: grid;
grid-template-rows: 7.5rem max-content 1fr;
min-height: 100vh;
}
.header {
display: flex;
justify-content: center;
align-items: center;
background-color: blue;
color: yellow;
}
.header__title {
font-size: 2rem;
line-height: 2.25;
text-transform: uppercase;
}
.navbar {
display: flex;
background: lightblue;
border-top: 0.125rem solid;
border-bottom: 0.125rem solid;
}
.navbar__item {
flex-grow: 1;
padding: .375rem .75rem;
text-align: center;
text-transform: capitalize;
text-decoration: none;
color: black;
font-size: 1.125rem;
line-height: 1.5;
}
.content {
background: silver;
padding: 1.5rem;
}
.content__title {
font-size: 1.125rem;
line-height: 2.25;
margin-top: 0;
margin-bottom: .75rem;
}
<main class="main">
<header class="header">
<h1 class="header__title">Title</h1>
</header>
<nav class="navbar">
<a href="#" class="navbar__item">item</a>
<a href="#" class="navbar__item">item</a>
<a href="#" class="navbar__item">item</a>
<a href="#" class="navbar__item">item</a>
<a href="#" class="navbar__item">item</a>
<a href="#" class="navbar__item">item</a>
<a href="#" class="navbar__item">item</a>
</nav>
<article class="content">
<h2 class="content__title">Lorem Ipsum</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Cupiditate at omnis hic, maxime ab iure facilis. Dolore alias veniam nisi doloribus at corrupti sapiente ipsam quo voluptates? Excepturi, mollitia qui!</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Cupiditate at omnis hic, maxime ab iure facilis. Dolore alias veniam nisi doloribus at corrupti sapiente ipsam quo voluptates? Excepturi, mollitia qui!</p>
</article>
</main>
Simple enough, right? The conversion to rem
is basically the pixel value divided by 16 (the default root font-size). Now adding just a single line, we can scale everything:
html { font-size: 32px; } /* added just that, doubling the scale (16*2 = 32) */
body {
margin: 0;
font-family: sans-serif;
line-height: 1.5;
}
p {
margin-top: 0;
margin-bottom: .75rem;
font-size: 1rem;
line-height: 1.5;
}
.main {
display: grid;
grid-template-rows: 7.5rem max-content 1fr;
min-height: 100vh;
}
.header {
display: flex;
justify-content: center;
align-items: center;
background-color: blue;
color: yellow;
}
.header__title {
font-size: 2rem;
line-height: 2.25;
text-transform: uppercase;
}
.navbar {
display: flex;
background: lightblue;
border-top: 0.125rem solid;
border-bottom: 0.125rem solid;
}
.navbar__item {
flex-grow: 1;
padding: .375rem .75rem;
text-align: center;
text-transform: capitalize;
text-decoration: none;
color: black;
font-size: 1.125rem;
line-height: 1.5;
}
.content {
background: silver;
padding: 1.5rem;
}
.content__title {
font-size: 1.125rem;
line-height: 2.25;
margin-top: 0;
margin-bottom: .75rem;
}
<main class="main">
<header class="header">
<h1 class="header__title">Title</h1>
</header>
<nav class="navbar">
<a href="#" class="navbar__item">item</a>
<a href="#" class="navbar__item">item</a>
<a href="#" class="navbar__item">item</a>
<a href="#" class="navbar__item">item</a>
<a href="#" class="navbar__item">item</a>
<a href="#" class="navbar__item">item</a>
<a href="#" class="navbar__item">item</a>
</nav>
<article class="content">
<h2 class="content__title">Lorem Ipsum</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Cupiditate at omnis hic, maxime ab iure facilis. Dolore alias veniam nisi doloribus at corrupti sapiente ipsam quo voluptates? Excepturi, mollitia qui!</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Cupiditate at omnis hic, maxime ab iure facilis. Dolore alias veniam nisi doloribus at corrupti sapiente ipsam quo voluptates? Excepturi, mollitia qui!</p>
</article>
</main>
Edit: Okay, I see it now - after the discussion on the comments. If your hardware is the one doing the scaling, and not the browser, I believe you are out of luck. But it should be possible to configure your monitors to extend one another horizontally instead, which would make it a proper 7680x1080 canvas that you could detect with a media-query for a 64:9 aspect ratio. Most OS will have that option (excluding things like windows starter). Ubuntu is a free option that have it out of the box, for example. I just assumed that's what you had, since it's usually the simplest way to go.