Today I Learned

hashrocket A Hashrocket project

92 posts about #html-css surprise

Break words in Tailwind.

In Tailwind you can use certain classes to wrap text within an element.
You could use something like text-wrap or whitespace-pre-wrap, or any of the other classes.

However, if a single word is too long it will continue on past the element it is contained in.

Kind of like this:

<div class="text-wrap">
<!-- really long text -->
</div>

HEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGword

Well, just add the Tailwind class break-words and it will break up a word when it reaches the end of the element, and you'll get something more like this:

<div class="text-wrap break-words">
<!-- really long text -->
</div>

HEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGword HEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGword

Open New Tab with HTML Form Submit

You can open a new tab on form submit by changing the target attribute of the form element:

<form target="_blank">
<!-- ... -->

</form>

While this is useful, it doesn't handle the scenario where you have multiple submit buttons (different actions) that need to behave differently.

For that, there's the formtarget attribute on your submit button/input. Note that by setting this attribute, you will override the form target attribute if set.

In the example below, the "Save" button will submit the form in the current window, while the Preview window will submit the form in a new tab

<form action="/liquid-templates/1">
<!-- ... -->


  <button type="submit">Save</button>
  <button type="submit" formaction="/liquid-templates/1/preview" formtarget="_blank">Preview</button>
</form>

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#target https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#formtarget

Styling your host from within shadow dom

The :host declaration in CSS is a functional pseudo-class used in Web Components, specifically within Shadow DOM. When you create a custom element with Shadow DOM, the :host pseudo-class allows you to define styles that will apply to the host element of the shadow tree. This means you can style the custom element rather than just its content. For instance, if you create a custom <my-element> tag and attach a shadow DOM to it, using :host in your CSS (inside the shadow DOM) lets you set properties like background color, margin, or padding directly on <my-element>. This is particularly useful for encapsulating styles in web components, as it allows the component to have its own isolated styling that doesn't bleed into the rest of the page or get affected by external styles.

CSS text-wrap now with more pretty

There are several ways to get your text to wrap with css. However it always seems that there ends up being an orphan piece of text on the wrap. The newly introduced pretty option for text-wrap aims to fix this.

A comparison of a paragraph with orphans and one with no orphans, each with a badge of bad or good.

Image sourced fromΒ Why you should remove orphans from your body text.

Like i mentioned earlier it is a new feature and is not fully supported in all browsers yet but definitely a welcome addition.

Style elements on data attributes in Tailwind

Using Tailwind you can apply custom styling to an element based on it's data attribute.

For example, given these elements:

<div data-state="checked" class="data-[state=checked]:bg-black bg:white">
<!-- Html that could change the data-state goes here -->

</div>

<div data-state="unchecked" class="data-[state=checked]:bg-black bg:white">
<!-- Html that could change the data-state goes here -->

</div>

Of these two elements, the first one would be given a black background, while the second one would have a white background, unless it's data-state changes to checked.

Nesting supported in CSS

One of the main reasons I used Sass was just to get nesting of my styles. Apparently it was finally rolled into CSS.

So what you had before:

.nesting {
  color: hotpink;
}

.nesting > .is {
  color: rebeccapurple;
}

.nesting > .is > .awesome {
  color: deeppink;
}

Now turns in to:

.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

Find out more at the Chrome developer page for CSS Nesting

Do not serve SVG images in email content

Turns out that even with modern email we cannot rely on SVG file support.

The Google image proxy that every image gets served by when mail is processed through Gmail will not serve your SVG content.

This is a long standing issue and apparently will not be fixed.

You can possibly use inline SVG or a failover via the image tags src & srcset attributes, however, just converting to PNG/JPG will end up being the least problematic.

Arbitrary Values with Tailwind

You can set custom values in tailwind, without customizing your theme like this:

<div class="w-[50rem]">...</div>

This bracket syntax works with basically anything in tailwind. For example, here's font size.

<p class="text-[14px]">...</p>

This is super helpful at times when you don't want to bother with tailwind's size classes.

Control printable backgrounds in CSS

Have you created an amazing print version of your html page and when you go to print it out, it's not looking quite the same?

Hey those background colors are all missing!

Turns out that your user-agent (browser) is given control on how your elements should appear in different scenarios, like printing.

This is why when you print out a page with a background image, said image is generally not printed and you get a clean print.

Here the stamp style looks super elegant with our cornflowerblue background but when printing that we lose the look of the stamp due to the background being missing.

.stamp {
  background-color: cornflowerblue;
  border-radius: 5px;
  padding: 10px;
}

There is a way to control this though. The print-color-adjust css directive can be set so that your styles will do your printable bidding.

By default print-color-adjust is set to economy. I like to think of this as if we're saving money in unused ink.

With a small change to:

.stamp {
  background-color: cornflowerblue;
  border-radius: 5px;
  padding: 10px;
  print-color-adjust: exact;
}

We now get our stamp style that looks great in our browser to look great on a printed page as well.

!important classes in tailwind

If you need to add !important to one of your Tailwind classes, you simply need to add ! before the class name.

Here is a crude example:

Blue Background πŸ”΅

<div class="bg-red-500 bg-blue-500">
<!-- Insert content here -->

</div>

Red Background πŸ”΄

<div class="!bg-red-500 bg-blue-500">
<!-- Insert content here -->

</div>

When using this with responsive breakpoints, place it after the variant modifier.

Valid 😁

<div class="md:!bg-red-500">
<!-- Insert conent here -->

</div>

Invalid 😒

<div class="!md:bg-red-500">
<!-- Insert content here -->

</div>

SVG using href must be served from same site url

We like to use SVG sprites

However, if you utilize a cdn or serve assets from an external source... you may run into some issues. Hopefully you find this and it saves you time that we lost figuring this out

According to MDN:

For security reasons, browsers may apply the same-origin policy on use elements and may refuse to load a cross-origin URL in the href attribute. There is currently no defined way to set a cross-origin policy for use elements.

!important property

If you ever find yourself with conflicting rules on an element, and want one to take precedence even over rules with higher specificity. you can add the !important property, to override all other rules.

Here is an example:

#id {
  background-color: blue;
}

.class {
  background-color: gray;
}

p {
  background-color: red !important;
}
&lt;p>This is some text in a paragraph.&lt;/p>

&lt;p class="class">This is some text in a paragraph.&lt;/p>

&lt;p id="id">This is some text in a paragraph.&lt;/p>

In this case, all of these &lt;p> tags will have a red background.

CSS Filter Property

There is a CSS property filter which allows you to manipulate properties of an element such as their blur, brightness, contrast, grayscale, opacity, and more. This is most often applied to images, but does work on any element.

.single-filter {
  filter: opacity(50%);
}
.multiple-filters {
  filter: blur(5px) grayscale(100%);
}

Here comes our friendly neighborhood Pikachu to help show us what this looks like: image

Here is a good reference for what filter can do.

CSS target pseudo class

You can style an element with css if its id attribute matches the window location's hash using the :target pseudo-class:

<style>
#content {
  display: none;
}
#content:target {
  display: block
}
</style>

<a href="#content">See hidden content</a>

<table id="content">
  <legend>Keep is secret!
  <tr>
    <td>Keep it safe!
</table>

https://developer.mozilla.org/en-US/docs/Web/CSS/:target

Override a form's action url with `formaction`

Recently, I found myself with a form that needed to be able to submit to multiple urls. You can definitely use some javascript to get around this but that seemed like overkill for my situation. That's when I learned about the HTML formaction attribute.

You can use this attribute on a submit tag and it will override the action specified by the form. Here's an example with a Ruby on Rails form, but you just as easily do something similar with plain HTML -

<% form_with url: false do |f| %>
  <% users.each do |user| %>
    <%= check_box_tag "user_ids[]", user.id, false %>
  <% end %>

  <%= f.submit "Assign to Users", formaction: "/users/assign" %>
  <%= f.submit "Print for Users", formaction: "/users/print" %>
<% end %>

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attr-formaction

HTML Tab Index Gotcha

The tabindex attribute on HTML elements behaves differently than one might first expect.

The default tabindex for an element is 0, all elements with this index will be tabbed to in the order they appear in the document.

Giving an element a positive tabindex will prioritize it over all elements with a 0 index, in the order they appear.

Consider the following code

<ul>
  {
    [0,1,2].map((n) => (
      <li><button tabindex={n}>{n}</button></li>
    )
  }
</ul>

The order that the buttons will be focused when tabbing is 1, then 2, and finally 0.

Use the <datalist> element for input suggestions

HTML5 has a <datalist> element which you can you use to create suggestions for inputs.

Just specify the list attribute on your input, whose value should correspond to the id of the datalist, and then populate your datalist with options:

<input list="programming-languages" id="programming-language-choice" name="programming-language-choice" />

<datalist id="programming-languages">
    <option value="Ruby">
    <option value="Elixir">
    <option value="Java">
</datalist>

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist

Finding a Data Attribute

Some codebases use data attributes for automated testing, and today I learned how to access and manipulate these attributes from a DevTools console.

document.querySelector(`[data-test-attribute="submit-button"]`).click()

This technique lets me visually verify that the attribute is in the right place and behaves as test software expects it should, without reading the HTML at all.

Base64 Encode Your Images

This is a companion to my previous post about inline HTML styles. If you want to put your images inline in your HTML, GNU coreutils provides the base64 program.

Here's me running it in Vim command mode, sending the output right to my current line:

:.! base64 -w 0 public/images/bg.gif

The -w 0 disables line wrapping, which I don't want for inline images. Prefix this string with "data:image/<MIME TYPE>;base64," and you're good to go.

What Styles are Being Used?

I like my exceptional pages (404, 500, 503) to rely as little as possible on the webserver to render in the browser. A nice tool to support this is CSS Used for Chrome.

CSS Used adds a panel to DevTools that reports which CSS classes from external stylesheets are being used on your page. You can then paste these styles inside a style tag and remove stylesheet references in your HTML. I iterate on this by opening the HTML file in my browser outside of my project repo; when all the styles are loading, I've removed the stylesheet dependency.

image

Byline Class and Time Tag πŸ“°

I've been hacking on TIL to optimize our site for Safari Reader mode. A lot of this is black box, because Apple doesn't have easy-to-find documentation about how the feature works.

One thing that seems to matter is a class called byline for your author information, and an HTML tag time for the publication date. Here's an example, borrowed from this site:

<p class="byline">
  <%= name %>
  <time datetime="<%= inserted_at %>">
    <%= inserted_at %>
  </time>
</p>

It gives us relevant reader information, shown here in Safari:

image

Here's the PR:

https://github.com/hashrocket/tilex/pull/650

Meta Charset Top That! πŸ”

When writing HTML, make sure that your meta charset tag is in the first 1024 bytes on the page.

Per MDN:

The <meta> element declaring the encoding must be inside the <head> element and within the first 1024 bytes of the HTML as some browsers only look at those bytes before choosing an encoding.

What does this mean practically? Keep this tag near the top of your document, and use a website validator like https://validator.w3.org to check your tag placement.

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta

Type "text/javascript" is Redundant

Have a script tag in your HTML annotated with <script type="text/javascript">? Per the HTML5 specification, the MIME type of a script tag, when omitted, is text/javascript. We are encouraged to omit it.

Delete that code and go green πŸ’š.

Per MDN:

Omitted or a JavaScript MIME type: This indicates the script is JavaScript. The HTML5 specification urges authors to omit the attribute rather than provide a redundant MIME type. In earlier browsers, this identified the scripting language of the embedded or imported (via the src attribute) code. JavaScript MIME types are listed in the specification.

Source:

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script

How to change the placeholder text color

The TextInput component in react-native has a property of placeholderTextColor that allows you to configure the placeholder text color! However, react-native-web does not have this property and it must be done in css. So if you were using react-native-web you would need to include .css file:

// screen.tsx
import "./styles.css";
import { TextInput } from "react-native";

const Screen = () => (
  <TextInput
    testID="change-placeholder-color"
    placeholder="enter some text"
    defaultValue=""
  />
);
/************* 
*
*  styles.css
*
**************/
[data-testid="change-placeholder-color"]::-webkit-input-placeholder {
  color: skyblue;
}
[data-testid="change-placeholder-color"]::-moz-placeholder {
  color: skyblue;
}
[data-testid="change-placeholder-color"]::-ms-placeholder {
  color: skyblue;
}
[data-testid="change-placeholder-color"]::placeholder {
  color: skyblue;
}

Force Code Block Length with Non-Breaking Space πŸ“

Services that render fenced code blocks tend to remove whitespace. They'll display these two code blocks the same way, trimming the blank lines from the second example.

let something;
let something;

GitHub does this, as does Deckset. Deckset also adjusts your code's font size based on how many lines are in the block, which means these two code blocks would have different font sizes on their slides. Sometimes I don't want that to happen, like when building a series of slides that fill in different pieces of an example and should look consistent.

I cheat this feature putting a non-breaking space on the last line. On Mac, I can do this with OPTION + SPACE. I can see the character in Vim ('+'), but it's invisible on the slide, and it prevents Deckset from collapsing the line it's on, forcing the code block to the length I choose.

SO: Use non-breaking spaces

Override Markdown Listing πŸ“

Markdown parsers have a neat feature: a list of any numbers tends to be turned into an ordered lists.

I write:

99. First thing (?)
1. Second thing (??)
50. Third thing (??)
1. Fourth thing (????)

And TIL's markdown parser (Earmark) produces this as HTML:

  1. First thing (?)
  2. Second thing (??)
  3. Third thing (??)
  4. Fourth thing (????)

Pretty cool! Sometimes I want to do my own thing, though. This Stack Overflow question elicits a bunch of techniques; the kind I learned today and used with success is:

99\. First thing (?)
1\. Second thing (??)
50\. Third thing (??)
1\. Fourth thing (????)

Escape the pattern; break the system! This perserved my unordered numbering. Now, I may have other problems, like losing automatic ol and li tags, but the hack works.

`user-select:none` needs prefixes for each browser

The user-select css property governs if text is selectable for a given element. user-select: none means that it is not selectable.

What's interesting about this property is that while each browser supports it, they each require their own prefix, except Chrome, which does not need a prefix.

In create react app, what starts out as:

user-select: none;

Gets expanded to:

-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;

But the default browserList configuration for development in your package.json file is:

"development": [
    "last 1 chrome version",
    "last 1 firefox version",
    "last 1 safari version"
]

And so in development, it gets expanded to:

-webkit-user-select: none;
-moz-user-select: none;
user-select: none;

Sorry Micrsoft.

How does create react app know which prefixes to render? The caniuse-lite npm package has up-to-date support data for each property and user-select:none is defined here.

That data is compressed. Here's how to uncompress it using the node cli to see what it represents:

const compressedData = require('caniuse-lite/data/features/user-select-none');
const caniuse = require('caniuse-lite');
const unpackFunction = caniuse.feature;
unpackFunction(compressedData);

This is accomplished in create react app by the npm package autoprefixer;

Highlight Diffs With Markdown

Today I learned about the 'diff' syntax, supported by many syntax highlighting libraries including those used by Github and Today I Learned.

Add diff to your fenced code block, and this:

def example do
-  :ok
+  {:ok, :more_stuff}
end

Becomes this:

def example do
-  :ok
+  {:ok, :more_stuff}
end

A nice use case is a README or pull request description; show instead of tell the changes you made, or someone should make.

Ad block is hiding your selector

Why is .ad-inner set to display: none !important? It's because I'm using uBlock Origin, a fantastic ad blocker. It makes the internet tolerable. But it completely breaks the site I'm working on because a previous dev on the project chose ad-inner as a class name.

It turns out, there are a lot of class names that you shouldn't use. Class names, ids, even certain dimensions applied as inline styles. uBlock Origin sources its rules from about 8 different lists, and the list that contained a rule for .ad-inner is EasyList. EasyList is hosted on github and the particular file with selector rules is easylist_general_hide.txt

Here are all the class names in the list that begin with .ad-i

##.ad-icon
##.ad-identifier
##.ad-iframe
##.ad-imagehold
##.ad-img
##.ad-img300X250
##.ad-in-300x250
##.ad-in-content-300
##.ad-in-post
##.ad-in-results
##.ad-incontent-ad-plus-billboard-top
##.ad-incontent-ad-plus-bottom
##.ad-incontent-ad-plus-middle
##.ad-incontent-ad-plus-middle2
##.ad-incontent-ad-plus-middle3
##.ad-incontent-ad-plus-top
##.ad-incontent-wrap
##.ad-index
##.ad-index-main
##.ad-indicator-horiz
##.ad-inline
##.ad-inline-article
##.ad-inner
##.ad-inner-container
##.ad-innr
##.ad-inpage-video-top
##.ad-insert
##.ad-inserter
##.ad-inserter-widget
##.ad-integrated-display
##.ad-internal
##.ad-interruptor
##.ad-interstitial
##.ad-intromercial
##.ad-island
##.ad-item
##.ad-item-related

A good rule of thumb is to not start a class name with .ad. This survey says that 30% of users are using ad blockers.

`::before` pseudo element full height

I'm trying to put a before image to the left of some text. But I want the text to NOT wrap below the image. The image is only 40px tall but the text can be any height.

<div class="text">
  Many many words
</div>
.text {
  width: 200px;

  &:before {
    background-image: url('carrot.png');
  }
}

When I do this the words wrap under the carrot. To fix this I can use the absolute top 0 bottom 0 technique to give the before pseudo element full height.

.text {
  width: 200px;
  position: relative;
  padding-left: 32px;

  &:before {
    background-image: url('carrot.png');
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
  }
}

Now none of the text wraps under the carrot.

Submit A Form With A Button Outside The Form

You can tie a submit button to a form that the button doesn't live inside of. The trick is to give the form an id and then reference that id with the button's form property.

<div>
  <form id="my-form">
    <label for="name">Name:</label>
    <input type="text" name="name"></input>
  </form>

<!-- ... -->


  <button type="submit" form="my-form">Submit</button>
</div>

With this setup, clicking the Submit button will cause the form to be submitted.

See the MDN Button docs for more details.

Prevent a User From Selecting Text

Today I learned about a CSS property called user-select. You might use it like this, to prevent a user from selecting text:

p {
  user-select: none;
}

I discovered this on an event booking website: the event description links were not anchor tags, and the content on the page was not selectable.

If you're going to disable default behavior on a website, you need to have a good reason. My assumption (which could be wrong) is that the site creators don't want me to select text on the page and start searching around for a better deal. I don't think that's a good enough reason, because there are plenty of cases where a user might copy text on a page that don't hurt (or actually support) the event organizer's business.

docs

Separated Table Borders

Today I learned how to make separated table borders, like this:

border-collapse

Each td has a top and bottom border, and these settings make the separation possible:

table {
  border-collapse: separate;
  border-spacing: 3rem 0rem;
}

The border-collapse property sets whether cells inside a table have shared or separate borders. The border-spacing property sets the distance between the borders of adjacent table cells, when border-collapse is set to separate.

More info: table properties