Css Tricks

Syndicate content
Tips, Tricks, and Techniques on using Cascading Style Sheets.
Updated: 16 hours 10 min ago

What I learned by building my own VS Code extension

Mon, 08/20/2018 - 4:05am

VS Code is slowly closing the gap between a text editor and an integrated development environment (IDE). At the core of this extremely versatile and flexible tool lies a wonderful API that provides an extensible plugin model that is relatively easy for JavaScript developers to build on. With my first extension, VS Code All Autocomplete, reaching 25K downloads, I wanted to share what I learned from the development and maintenance of it with all of you.

Trivia! Visual Studio Code does not share any lineage with the Visual Studio IDE. Microsoft used the VS brand for their enterprise audience which has led to a lot of confusion. The application is just Code in the command line and does not work at all like Visual Studio. It takes more inspiration from TextMate and Sublime Text than Visual Studio. It shares the snippet format of TextMate (Mac only) and forgoes the XML based format used in Visual Studio.

Why you should create an extension

On the surface, VS Code does not seem to provide many reasons for why anyone would create extensions for it. The platform has most of the same features that other editors have. The snippet format is powerful, and with extensions like Settings Sync, it is easy to share them over a Gist. Code (pun intended) is open source and the team is fairly responsive to requests. Basic support can be provided without a plugin by creating a "typings" file in the npm module.

Still, creating extensions for VS Code is something all web developers should try for the following reasons:

  • Playing with TypeScript: Many developers are still on the edge with TypeScript. Writing an extension in VS Code gives you a chance to witness how TypeScript works in action and how much its type safety and autocomplete features can help with your next JavaScript project.
  • Learning: The web gets a lot of slack for its performance. VS Code demonstrates how you can develop a performance-sensitive applications in Electron and some of the techniques — the multi-process and cluster-oriented architecture is so good that all Electron apps should just steal it for their own use.
  • Fun: Most important, it is a lot of fun developing VS Code extensions. You can scratch an itch and end up giving back to the community and saving time for so many developers.

Trivia! Even though TypeScript is an independent programming language with many popular uses including the Angular (2+) framework, VS Code is the sister project with the most impact on the language. Being developed in TypeScript as a TypeScript editor, VS Code has a strong symbiotic relationship with TypeScript. The best way to learn the language is by looking at the source code of Visual Studio Code.

What an extension can do

VS Code exposes multiple areas where an extension can create an impact. The exposed touch points could easily fill a book. But there’s unlikely a situation where you need all of them, so I’ve put together a listing here.

These outline the various places that can be extended as of version 1.25:

Area Description Language grammar Variables, expressions, reserved words, etc. for highlighting Code Snippets Items in autocomplete with tab-based navigation to replace certain items Language configuration Auto-close and indent items like quotes, brackets, etc. Hover action item Documentation tooltip on hover Code Completion Autocomplete items when typing Error Diagnostics Squiggly red underlines to indicate potential errors Signature Helper Method signature tooltip while typing Symbol Definition Location of the code where the symbol is defined in the inline editor and go to the definition (including a ?+Hover tooltip) Reference For the inline editor with links, all files, and places associated with the symbol Document Highlighter List of all positions where the selected symbol exists for highlighting Symbol List of symbols navigable from the Command menu with an @ modifier Workspace Symbol Symbol provider for an entire workspace Code Action Squiggly green underline to indicate fixable errors with a fix action on click Code Lens Always present inline metadata with clickable action items Rename Support for renaming symbols used in multiple places Document Formatting Fix indentation and formatting for the entire document Document Range Formatting Fix indentation and formatting for selected text On Type Formatting Fix formatting and indentation in real time Color Provider Color popup menu alternative Configuration Defaults Override settings and save as a new set of defaults Command Commands in the ?+P menu Menu Menu items in the top level menu, any menus alongside the document tab bar, and context menus Key bindings Keyboard shortcuts Debugger Debugger settings for debugging a new or existing language Source Control Overrides to support a custom source control system Theme Color theme Snippet Same as code snippets above View A new section in one of the docked panels on the left View Containers A new docked panel on the left bar TypeScript server plugins Overrides for the built-in TypeScript language server Webview Alternative page in parallel to a document to show a custom rendering of a document or any custom HTML (unlike a View Container that is docked to a side) Text Decoration Decoration on the gutter of the text area Messages Popups for error, warning and informational messages on the bottom-right Quick Pick Multi-select option selector menu Input Box Text box for the user to input values Status Bar Item Icon, button, or text in the status bar Progress Show a progress indicator in the UI Tree View Create a tree like the one used to define the workspace (which can be put inside a View or a View Container) Folding Range Custom code folding into the plus button on the gutter Implementation The implementation provider (languages like and TypeScript can have declaration and implementation as separate) Diff Provider Diff view in source control mode Commit Template For commits in source control mode

Apart from this, you can invoke any functionality that the user can invoke in any of the menus with whatever parameters the user can pass. There are events on almost every functionality as well as a lot of file systems and text file-related utility methods.

Let’s start building

Alright, enough with the preamble — let’s start putting an extension together with what we’ve just learned.

Creating the extension

To build an extension, start with the VS Code extension generator.

npm install -g yo generator-code yo code

Next up, configure the options. Here’s how I set things up:

Extension Generator for VS Code

TypeScript is optional, but highly recommended. Just another way to remind you that VS Code works really well with TypeScript.

Now, you can open the folder in VS Code. I would recommend checking the initial commit. You can look at the quick start guide that gets generated in the project to understand the file structure. Take a quick stab at seeing it live. Go to the debug panel and launch the extension in debug mode. Place a breakpoint inside the activate method in extension.ts to walk through this in action. The “Hello world" application already registers a command that can launch an informational message in the footer. Go to the commands menu with ?+?+P (Ctrl+Shift+P on Windows) and select hello world.

Hitting the debugger

You can put another breakpoint in the registerCommand callback to get the command event.

Open package.json for a description of the plugin’s configuration. Change activationEvents to a more specific one based on what type of language or file protocol you want to support. All Autocomplete supports all file formats, indicated by an *. You should also look at the contributes section if you want to contribute to things like settings, commands, menu items, snippets, etc.

All Autocomplete in the contributes section with contribution points

Many of these contributions have additional JSON in the same package.json file with all the information. Some APIs require code that has to be used in the activate call like vscode.commands.registerCommand for creating a command. For an autocomplete extension, the contributes section is not required and can be removed.

To use the same All Autocomplete options in extension.ts, replace the activate function with the following:

export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.languages.registerCompletionItemProvider('*', { provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken) { return [new vscode.CompletionItem("Hello")]; } })); }

You can specify further details about the completion item, like attached documentation using more options in the object. Now, if you debug this and type H you should see Hello in the completion menu. The code to register most of the language-based providers is nearly the same.

The All Autocomplete menu

You can see the All Autocomplete menu in vscode.languages, which provides options to register providers. Each provider has its own set of parameters that we can fill up similar to the completion item provider.

All Autocomplete with the list of language-specific providers

The document object provides access to the document with utility methods to access text at specific positions and ranges. It is strongly encouraged to use the APIs to access all documents instead of the raw Node.js APIs.

You can parse the document on demand or keep a data structure (like the trie used in All Autocomplete) optimized to search for inputs as the user is typing.

Tip: If you are looking for some text selection/manipulation APIs, there will most likely be an API already available. No need to reinvent the wheel. You can precisely get text with document.getText(document.getWordRangeAtPosition(position)). Alt+Click on any VS Code object to get to the class structure and JSDoc documentation.

Publishing the extension

Once the extension is complete, it is time to publish it to the marketplace. VS Code has a command line tool (vsce) for publishing but it does require creating an account.

Here’s how to prep the extension for submission:

  • Clean up the package: The package.json and README.md files provide the description and details about your extension that get displayed in the marketplace. It is essential to spruce up those files and fill all missing information so that the documentation comes out clean. Good to add some badges and a self-describing GIF to the repo.
  • Create an account: You need to create a Visual Studio Team Services (VSTS) account. This is the only place where VS Code ties up with Visual Studio. You need to sign up and get an access token. The VSTS interface is slightly confusing, but you don’t need to learn a new code management tool to publish. Go to the security section to get the access token. (Don’t make the same mistake as me and confuse the gear icon in the menu with security.)
  • Install: Use the vsce command line tool to publish extensions. It is available in npm and is extremely easy to use.
Security Settings in VSTS

Tip: The access token for VSTS expires every year and therefore the account information is extremely important. It is also required to reply to the comments on the marketplace, though most users are active on GitHub and that is where you are more likely to get bugs and feature requests.

npm install -g vsce # One time installation vsce create-publisher <name> # One time create publisher vsce login # One time login. Asks for access token. vsce publish <version> # Publish or update the extension

VS Code does not compile extensions on the server. Make sure the output folder created by compiling your extension is up to date. Also be sure to check case sensitivity of your file names because incorrect file paths will break in Linux. Native modules in Node are a huge pain and should not be used. It is impossible to compile and provide all platform variants for specific Electron versions. (Someone needs to create a PhoneGap build for npm!) This will get better over time with WebAssembly and N-API.

Support and Maintenance

The VS Code team is super active on GitHub and StackOverflow. GitHub is the right place to file bugs that you might discover in the API. The team is fairly responsive though you need to make the context extremely clear as you would with any helpful bug report.

You should have a GitHub repository for your extension and expect users to file issues directly on GitHub. Expect VS Code users to be proficient with tools and technology (some of them may have a lot more knowledge that you). Even though it’s a free endeavor, keeping humility and treating issue reporters as customers is the right behavior.

Tips on performance

VS Code has good performance because it is built with an architecture that isolates things like extensions that can cause slowness. If your extension does not return in time, your output might be ignored.

A few things that can help maintaining the performance of the editor include:

  • Using the official APIs: It is easy to ignore them and build your own. The "typings" file is wonderful and has documentation for all of the available APIs. A five minute search there can save a lot of time. If you need some files, it is better in most cases to request VS Code to open it in an editor than it is to read it from a disk (unless you are reading thousands of files and not leaving them open).
  • Expose options: Ensure there is a way for users to turn off capabilities that rely on heavy events, like every keystroke. It may not seem noticeable on your machines, but this is not the place for that. Developers maintain their dot files forever and they spend time going through options if there is an issue they to work around. There is no harm in exposing a way to gracefully degrade in case every keystroke is not possible.
  • Child process: Developer tools — especially on the command line — are extremely fast and well optimized. If you need something that involves a lot of files that could choke the JavaScript thread, call into a native tool and politely ask the users to install the dependency. Electron has limitations and we should accept them.
Wrapping up

VS Code is a very flexible application and provides almost all of its guts to extension developers. The workflow to develop and debug extensions is easy and every JavaScript developer should feel at home trying them out. The biggest advantage of developing extensions outside of improving our own lives is the possibility to see a huge TypeScript project in action. For that alone, I would recommend all users to definitely give it a go and share your extensions in the comments.

My extension is live on the VS Code marketplace and I would love to get your feedback on that as well. &#x1f642;

The post What I learned by building my own VS Code extension appeared first on CSS-Tricks.

Firefox Multi-Account Containers

Fri, 08/17/2018 - 10:20am

It's an extension:

Each Container stores cookies separately, so you can log into the same site with different accounts and online trackers can’t easily connect the browsing.

A great idea for a feature if you ask me. For example, I have two Buffer accounts and my solution is to use different browsers entirely to stay logged into both of them. I know plenty of folks that prefer the browser version of apps like Notion, Front, and Twitter, and it's cool to have a way to log into the same site with multiple accounts if you need to — and without weird trickery.

This is browsers competing on UI/UX features rather than web platform features, which is a good thing. Relevant: Opera Neon and Refresh.

Direct Link to ArticlePermalink

The post Firefox Multi-Account Containers appeared first on CSS-Tricks.

Seriously, though. What is a progressive web app?

Fri, 08/17/2018 - 4:14am

Amberley Romo read a ton about PWAs in order to form her own solid understanding.

“Progressive web app” (PWA) is both a general term for a new philosophy toward building websites and a specific term with an established set of three explicit, testable, baseline requirements.

As a general term, the PWA approach is characterized by striving to satisfy the following set of attributes:

  1. Responsive
  2. Connectivity independent
  3. App-like-interactions
  4. Fresh
  5. Safe
  6. Discoverable
  7. Re-engageable
  8. Installable
  9. Linkable

Direct Link to ArticlePermalink

The post Seriously, though. What is a progressive web app? appeared first on CSS-Tricks.

Level up your .filter game

Fri, 08/17/2018 - 3:45am

.filter is a built-in array iteration method that accepts a predicate which is called against each of its values, and returns a subset of all values that return a truthy value.

That is a lot to unpack in one statement! Let's take a look at that statement piece-by-piece.

  • "Built-in" simply means that it is part of the language—you don't need to add any libraries to get access to this functionality.
  • "Iteration methods" accept a function that are run against each item of the array. Both .map and .reduce are other examples of iteration methods.
  • A "predicate" is a function that returns a boolean.
  • A "truthy value" is any value that evaluates to true when coerced to a boolean. Almost all values are truthy, with the exceptions of: undefined, null, false, 0, NaN, or "" (empty string).

To see .filter in action, let's take a look at this array of restaurants.

const restaurants = [ { name: "Dan's Hamburgers", price: 'Cheap', cuisine: 'Burger', }, { name: "Austin's Pizza", price: 'Cheap', cuisine: 'Pizza', }, { name: "Via 313", price: 'Moderate', cuisine: 'Pizza', }, { name: "Bufalina", price: 'Expensive', cuisine: 'Pizza', }, { name: "P. Terry's", price: 'Cheap', cuisine: 'Burger', }, { name: "Hopdoddy", price: 'Expensive', cuisine: 'Burger', }, { name: "Whataburger", price: 'Moderate', cuisine: 'Burger', }, { name: "Chuy's", cuisine: 'Tex-Mex', price: 'Moderate', }, { name: "Taquerias Arandina", cuisine: 'Tex-Mex', price: 'Cheap', }, { name: "El Alma", cuisine: 'Tex-Mex', price: 'Expensive', }, { name: "Maudie's", cuisine: 'Tex-Mex', price: 'Moderate', }, ];

That's a lot of information. I'm currently in the mood for a burger, so let's filter that array down a bit.

const isBurger = ({cuisine}) => cuisine === 'Burger'; const burgerJoints = restaurants.filter(isBurger);

isBurger is the predicate, and burgerJoints is a new array which is a subset of restaurants. It is important to note that restaurants remained unchanged from the .filter.

Here is a simple example of two lists being rendered—one of the original restaurants array, and one of the filtered burgerJoints array.

See the Pen .filter - isBurger by Adam Giese (@AdamGiese) on CodePen.

Negating Predicates

For every predicate there is an equal and opposite negated predicate.

A predicate is a function that returns a boolean. Since there are only two possible boolean values, that means it is easy to "flip" the value of a predicate.

A few hours have passed since I've eaten my burger, and now I'm hungry again. This time, I want to filter out burgers to try something new. One option is to write a new isNotBurger predicate from scratch.

const isBurger = ({cuisine}) => cuisine === 'Burger'; const isNotBurger = ({cuisine}) => cuisine !== 'Burger';

However, look at the amount of similarities between the two predicates. This is not very DRY code. Another option is to call the isBurger predicate and flip the result.

const isBurger = ({cuisine}) => cuisine === 'Burger'; const isNotBurger = restaurant => !isBurger(restaurant);

This is better! If the definition of a burger changes, you will only need to change the logic in one place. However, what if we have a number of predicates that we'd like to negate? Since this is something that we'd likely want to do often, it may be a good idea to write a negate function.

const negate = predicate => function() { return !predicate.apply(null, arguments); } const isBurger = ({cuisine}) => cuisine === 'Burger'; const isNotBurger = negate(isBurger); const isPizza = ({cuisine}) => cuisine === 'Pizza'; const isNotPizza = negate(isPizza);

You may have some questions.

What is .apply?

MDN:

apply() method calls a function with a given this value, and arguments provided as an array (or an array-like object).

What is arguments?

MDN:

The arguments object is a local variable available within all (non-arrow) functions. You can refer to a function's arguments within the function by using the arguments object.

Why return an old-school function instead of a newer, cooler arrow function?

In this case, returning a traditional function is necessary because the arguments object is only available on traditional functions.

Added August 20th, 2018. As some commentors have correctly pointed out, you can write `negate` with an arrow function by using rest parameters.

Returning Predicates

As we saw with our negate function, it is easy for a function to return a new function in JavaScript. This can be useful for writing "predicate creators.” For example, let's look back at our isBurger and isPizza predicates.

const isBurger = ({cuisine}) => cuisine === 'Burger'; const isPizza = ({cuisine}) => cuisine === 'Pizza';

These two predicates share the same logic; they only differ in comparisons. So, we can wrap the shared logic in an isCuisine function.

const isCuisine = comparison => ({cuisine}) => cuisine === comparison; const isBurger = isCuisine('Burger'); const isPizza = isCuisine('Pizza');

This is great! Now, what if we want to start checking the price?

const isPrice = comparison => ({price}) => price === comparison; const isCheap = isPrice('Cheap'); const isExpensive = isPrice('Expensive');

Now the isCheap and isExpensive are DRY, and isPizza and isBurger are DRY—but isPrice and isCuisine share their logic! Luckily, there are no rules for how many functions deep you can return.

const isKeyEqualToValue = key => value => object => object[key] === value; // these can be rewritten const isCuisine = isKeyEqualToValue('cuisine'); const isPrice = isKeyEqualToValue('price'); // these don't need to change const isBurger = isCuisine('Burger'); const isPizza = isCuisine('Pizza'); const isCheap = isPrice('Cheap'); const isExpensive = isPrice('Expensive');

This, to me, is the beauty of arrow functions. In a single line, you can elegantly create a third-order function. isKeyEqualToValue is a function that returns the function isPrice which returns the function isCheap.

See how easy it is to create multiple filtered lists from the original restaurants array?

See the Pen .filter - returning predicates by Adam Giese (@AdamGiese) on CodePen.

Composing Predicates

We can now filter our array by burgers or by a cheap price... but what if you want cheap burgers? One option is to chain two filters together.

const cheapBurgers = restaurants.filter(isCheap).filter(isBurger);

Another option is to "compose" the two predicates into a single one.

const isCheapBurger = restaurant => isCheap(restaurant) && isBurger(restaurant); const isCheapPizza = restaurant => isCheap(restaurant) && isPizza(restaurant);

Look at all of that repeated code. We can definitely wrap this into a new function!

const both = (predicate1, predicate2) => value => predicate1(value) && predicate2(value); const isCheapBurger = both(isCheap, isBurger); const isCheapPizza = both(isCheap, isPizza); const cheapBurgers = restaurants.filter(isCheapBurger); const cheapPizza = restaurants.filter(isCheapPizza);

What if you are fine with either pizza or burgers?

const either = (predicate1, predicate2) => value => predicate1(value) || predicate2(value); const isDelicious = either(isBurger, isPizza); const deliciousFood = restaurants.filter(isDelicious);

This is a step in the right direction, but what if you have more than two foods you'd like to include? This isn't a very scalable approach. There are two built-in array methods that come in handy here. .every and .some are both predicate methods that also accept predicates. .every checks if each member of an array passes a predicate, while .some checks to see if any member of an array passes a predicate.

const isDelicious = restaurant => [isPizza, isBurger, isBbq].some(predicate => predicate(restaurant)); const isCheapAndDelicious = restaurant => [isDelicious, isCheap].every(predicate => predicate(restaurant));

And, as always, let's wrap them up into some useful abstraction.

const isEvery = predicates => value => predicates.every(predicate => predicate(value)); const isAny = predicates => value => predicates.some(predicate => predicate(value)); const isDelicious = isAny([isBurger, isPizza, isBbq]); const isCheapAndDelicious = isEvery([isCheap, isDelicious]);

isEvery and isAny both accept an array of predicates and return a single predicate.

Since all of these predicates are easily created by higher order functions, it isn’t too difficult to create and apply these predicates based on a user’s interaction. Taking all of the lessons we have learned, here is an example of an app that searches restaurants by applying filters based on button clicks.

See the Pen .filter - dynamic filters by Adam Giese (@AdamGiese) on CodePen.

Wrapping up

Filters are an essential part of JavaScript development. Whether you’re sorting out bad data from an API response or responding to user interactions, there are countless times when you would want a subset of an array’s values. I hope this overview helped with ways that you can manipulate predicates to write more readable and maintainable code.

The post Level up your .filter game appeared first on CSS-Tricks.

Working with refs in React

Thu, 08/16/2018 - 11:09am

Refs make it possible to access DOM nodes directly within React. This comes in handy in situations where, just as one example, you want to change the child of a component. Let’s say you want to change the value of an <input> element, but without using props or re-rendering the whole component.

That’s the sort of thing refs are good for and what we’ll be digging into in this post.

How to create a ref

createRef() is a new API that shipped with React 16.3. You can create a ref by calling React.createRef() and attaching a React element to it using the ref attribute on the element.

class Example extends React.Component { constructor(props) { super(props) // Create the ref this.exampleRef = React.createRef() } render() { return ( <div> // Call the ref with the `ref` attribute <input type="text" ref={this.exampleRef} /> </div> ) } }

We can "refer" to the node of the ref created in the render method with access to the current attribute of the ref. From the example above, that would be this.exampleRef.current.

Here’s an example:

See the Pen React Ref - createRef by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

class App extends React.Component { constructor(props) { super(props) // Create the ref this.textInput = React.createRef(); this.state = { value: '' } } // Set the state for the ref handleSubmit = e => { e.preventDefault(); this.setState({ value: this.textInput.current.value}) }; render() { return ( <div> <h1>React Ref - createRef</h1> // This is what will update <h3>Value: {this.state.value}</h3> <form onSubmit={this.handleSubmit}> // Call the ref on <input> so we can use it to update the <h3> value <input type="text" ref={this.textInput} /> <button>Submit</button> </form> </div> ); } } How a conversation between a child component and an element containing the ref might go down.

This is a component that renders some text, an input field and a button. The ref is created in the constructor and then attached to the input element when it renders. When the button is clicked, the value submitted from the input element (which has the ref attached) is used to update the state of the text (contained in an H3 tag). We make use of this.textInput.current.value to access the value and the new state is then rendered to the screen.

Passing a callback function to ref

React allows you to create a ref by passing a callback function to the ref attribute of a component. Here is how it looks:

<input type="text" ref={element => this.textInput = element} />

The callback is used to store a reference to the DOM node in an instance property. When we want to make use of this reference, we access it using:

this.textInput.value

Let's see how that looks in the same example we used before.

See the Pen React Ref - Callback Ref by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

class App extends React.Component { state = { value: '' } handleSubmit = e => { e.preventDefault(); this.setState({ value: this.textInput.value}) }; render() { return ( <div> <h1>React Ref - Callback Ref</h1> <h3>Value: {this.state.value}</h3> <form onSubmit={this.handleSubmit}> <input type="text" ref={element => this.textInput = element} /> <button>Submit</button> </form> </div> ); } }

When you make use of callback like we did above, React will call the ref callback with the DOM node when the component mounts, when the component un-mounts, it will call it with null.

It is also possible to pass ref from a parent component to a child component using callbacks.

See the Pen React Ref - Callback Ref 2 by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Let’s create our "dumb" component that will render a simple input:

const Input = props => { return ( <div> <input type="text" ref={props.inputRef} /> </div> ); };

This component is expecting inputRef props from its parent component which is then used to create a ref to the DOM node.

Here’s the parent component:

class App extends React.Component { state = { value: '' }; handleSubmit = event => { this.setState({ value: this.inputElement.value }); }; render() { return ( <div> <h1>React Ref - Callback Ref</h1> <h3>Value: {this.state.value}</h3> <Input inputRef={el => (this.inputElement = el)} /> <button onClick={this.handleSubmit}>Submit</button> </div> ); } }

In the App component, we want to obtain the text that is entered in the input field (which is in the child component) so we can render it. The ref is created using a callback like we did in the first example of this section. The key lies in how we access the DOM of the input element in the Input component from the App component. If you look closely, we access it using this.inputElement. So, when updating the state of value in the App component, we get the text that was entered in the input field using this.inputElement.value.

The ref attribute as a string

This is the old way of creating a ref and it will likely be removed in a future release because of some issues associated with it. The React team advises against using it, going so far as to label it as "legacy" in the documentation. We’re including it here anyway because there’s a chance you could come across it in a codebase.

See the Pen React Ref - String Ref by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Going back to our example of an input whose value is used to update text value on submit:

class App extends React.Component { state = { value: '' } handleSubmit = e => { e.preventDefault(); this.setState({ value: this.refs.textInput.value}) }; render() { return ( <div> <h1>React Ref - String Ref</h1> <h3>Value: {this.state.value}</h3> <form onSubmit={this.handleSubmit}> <input type="text" ref="textInput" /> <button>Submit</button> </form> </div> ); } }

The component is initialized and we start with a default state value set to an empty string (value='’). The component renders the text and form, as usual and, like before, the H3 text updates its state when the form is submitted with the contents entered in the input field.

We created a ref by setting the ref prop of the input field to textInput. That gives us access to the value of the input in the handleSubmit() method using this.refs.textInput.value.

Forwarding a ref from one component to another

**Ref forwarding is the technique of passing a ref from a component to a child component by making use of the React.forwardRef() method.

See the Pen React Ref - forward Ref by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Back to our running example of a input field that updates the value of text when submitted:

class App extends React.Component { constructor(props) { super(props) this.inputRef = React.createRef(); this.state = { value: '' } } handleSubmit = e => { e.preventDefault(); this.setState({ value: this.inputRef.current.value}) }; render() { return ( <div> <h1>React Ref - createRef</h1> <h3>Value: {this.state.value}</h3> <form onSubmit={this.handleSubmit}> <Input ref={this.inputRef} /> <button>Submit</button> </form> </div> ); } }

We’ve created the ref in this example with inputRef, which we want to pass to the child component as a ref attribute that we can use to update the state of our text.

const Input = React.forwardRef((props, ref) => ( <input type="text" ref={ref} /> ));

Here is an alternative way to do it by defining the ref outside of the App component:

const Input = React.forwardRef((props, ref) => ( <input type="text" ref={ref} /> )); const inputRef = React.createRef(); class App extends React.Component { constructor(props) { super(props) this.state = { value: '' } } handleSubmit = e => { e.preventDefault(); this.setState({ value: inputRef.current.value}) }; render() { return ( <div> <h1>React Ref - createRef</h1> <h3>Value: {this.state.value}</h3> <form onSubmit={this.handleSubmit}> <Input ref={inputRef} /> <button>Submit</button> </form> </div> ); } } Using ref for form validation

We all know that form validation is super difficult but something React is well-suited for. You know, things like making sure a form cannot be submitted with an empty input value. Or requiring a password with at least six characters. Refs can come in handy for these types of situations.

See the Pen React ref Pen - validation by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

class App extends React.Component { constructor(props) { super(props); this.username = React.createRef(); this.password = React.createRef(); this.state = { errors: [] }; } handleSubmit = (event) => { event.preventDefault(); const username = this.username.current.value; const password = this.password.current.value; const errors = this.handleValidation(username, password); if (errors.length > 0) { this.setState({ errors }); return; } // Submit data }; handleValidation = (username, password) => { const errors = []; // Require username to have a value on submit if (username.length === 0) { errors.push("Username cannot be empty"); } // Require at least six characters for the password if (password.length < 6) { errors.push("Password should be at least 6 characters long"); } // If those conditions are met, then return error messaging return errors; }; render() { const { errors } = this.state; return ( <div> <h1>React Ref Example</h1> <form onSubmit={this.handleSubmit}> // If requirements are not met, then display errors {errors.map(error => <p key={error}>{error}</p>)} <div> <label>Username:</label> // Input for username containing the ref <input type="text" ref={this.username} /> </div> <div> <label>Password:</label> // Input for password containing the ref <input type="text" ref={this.password} /> </div> <div> <button>Submit</button> </div> </form> </div> ); } }

We used the createRef() to create refs for the inputs which we then passed as parameters to the validation method. We populate the errors array when either of the input has an error, which we then display to the user.

That’s a ref... er, a wrap!

Hopefully this walkthrough gives you a good understanding of how powerful refs can be. They’re an excellent way to update part of a component without the need to re-render the entire thing. That’s convenient for writing leaner code and getting better performance.

At the same time, it’s worth heeding the advice of the React docs themselves and avoid using ref too much:

Your first inclination may be to use refs to "make things happen" in your app. If this is the case, take a moment and think more critically about where state should be owned in the component hierarchy. Often, it becomes clear that the proper place to "own" that state is at a higher level in the hierarchy.

Get it? Got it? Good.

The post Working with refs in React appeared first on CSS-Tricks.

Building Battleship in CSS

Thu, 08/16/2018 - 3:55am

This is an experiment to see how far into an interactive experience I can get using only CSS. What better project to attempt than a game? Battleship seemed like a good challenge and a step up from the CSS games I’ve seen so far because it has the complexity of multiple areas that have to interact with two players.

Wanna see the complete game?

View Repo View Demo

Oh, you wanna learn how it works? Let’s dig in.

I could tell right away there was going to be a lot of repetitive HTML and very long CSS selectors coming, so I set up Pug to compile HTML and Less to compile CSS. This is what all the code from here on is going to be written in.

Interactive elements in CSS

In order to get the game mechanics working, we need some interactive elements. We’re going to walk through each one.

HTML checkboxes and :checked

Battleship involves a lot of checking to see whether a field contains a ship or not, so we’re going to use a boatload of checkboxes.

[type*='checkbox'] { // inactive style &:checked { // active style } }

To style checkboxes, we would first need to reset them using appearance: none; which is only poorly supported right now and needs browser prefixes. The better solution here is to add helper elements. <input> tags can’t have children, including pseudo elements (even though Chrome will render them anyway), so we need to work around that using the adjacent sibling selector.

[type*='checkbox'] { position: relative; opacity: none; + .check-helper { position: absolute; top: 0; left: 0; pointer-events: none; // further inactive styles } &:checked { + .check-helper { // active styles } } }

If you use a <label> for the helper element, you will also extend the click area of the checkbox onto the helper, allowing you to position it more freely. Also, you can use multiple labels for the same checkbox. Multiple checkboxes for the same label are not supported, however, since you would have to assign the same ID for each checkbox.

Targets

We’re making a local multiplayer game, so we need to hide one player’s battlefield from the other and we need a pause mode allowing for a player to switch without glancing at the other player's ships. A start screen explaining the rules would also be nice.

HTML already gives us the option to link to a given ID in the document. Using :target we can select the element that we just jumped to. That allows us to create an Single Page Application-like behavior in a completely static document (and without even breaking the back button).

- var screens = ['screen1', 'screen2', 'screen3']; body nav each screen in screens a(href='#' + screen) each screen in screens .screen(id=screen) p #{screen} .screen { display: none; &:target { display: block; } } Visibility and pointer events

Rendering elements inactive is usually done by using pointer-events: none; The cool thing about pointer-events is that you can reverse it on the child elements. That will leave only the selected child clickable, but the parent stays click-through. This will come in handy later in combination with checkbox helper elements.

The same goes for visibility: hidden; While display: none; and opacity: 0; make the element and all it’s children disappear, visibility can be reversed.

Note that a hidden visibility also disables any pointer events, unlike opacity: 0;, but stays in the document flow, unlike display: none;.

.foo { display: none; // invisible and unclickable .bar { display: block; // invisible and unclickable } } .foo { visibility: hidden; // invisible and unclickable .bar { visibility: visible; // visible and clickable } } .foo { opacity: 0; pointer-evens: none; // invisible and unclickable .bar { opacity: 1; pointer-events: all; // still invisible, but clickable } } CSS Rule Reversible opacity Reversible pointer events display: none; ❌ ❌ visibility: hidden; ✅ ✅ opacity: 0;
pointer-events: none; ❌ ✅

OK, now that we’ve established the strategy for our interactive elements, let’s turn to the setup of the game itself.

Setting up

We have some global static variables and the size of our battlefields to define before we actually start:

@gridSize: 12; @zSea: 1; @zShips: 1000; @zAbove: 2000; @seaColor: #123; @enemyColor: #f0a; @playerColor: #0c8; @hitColor: #f27; body { --grid-measurements: 70vw; @media (min-aspect-ratio: 1/2) { --grid-measurements: 35vh; } }

The grid size is the size of the battlefield: 12x12 fields in this case. Next, we define some z-indexes and colors.

Here’s the Pug skeleton:

doctype html head title Ships! link(rel="stylesheet", href="style.css") meta(charset="UTF-8") meta(name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no") meta(name="theme-color" content="#000000") body

Everything HTML from this point on will be in the body.

Implementing the states

We need to build the states for Player 1, Player 2, pause, and a start screen. We’ll do this like it was explained above with target selectors. Here’s a little sketch of what we’re trying to achieve:

We have a few modes, each in its own container with an ID. Only one mode is to be displayed in the viewport—the others are hidden via display: none;, except for player modes. If one player is active, the other needs to be outside of the viewport, but still have pointer events so the players can interact with each other.

.mode#pause each party in ['p1', 'p2'] .mode(id=party) .mode#start .status each party in ['p1', 'p2'] a.player-link.switch(href='#' + party) a.status-link.playpause(href='#pause') End Turn h1 Ships!

The .status div contains the main navigation. Its entries will change depending on the active mode, so in order to select it properly, we’ll need put it after our .mode elements. The same goes for the <h1>, so it ends up at the end of the document (don’t tell the SEO people).

.mode { opacity: 0; pointer-events: none; &:target, &#start { opacity: 1; pointer-events: all; z-index: 1; } &#p1, &#p2 { position: absolute; transform: translateX(0); opacity: 1; z-index: 2; } &#p1:target { transform: translateX(50vw); +#p2 { transform: translateX(50vw); z-index: 2; } } &#p2 { transform: translateX(50vw); z-index: 1; } &#pause:target { ~ #p1, ~ #p2 { opacity: 0; } } } #start { .mode:target ~ & { display: none; } }

The .mode div never has pointer events and always is fully transparent (read: inactive), except for the start screen, which is enabled by default and the currently targeted screen. I don’t simply set it to display: none; because I still need it to be in the document flow. Hiding the visibility won’t work because I need to activate pointer events individually later on, when hitting enemy ships.

I need #p1 and #p2 to be next to each other because that’s what’s going to enable the interaction between one players hits and the other players ships.

Implementing the battlefields

We need two sets of two battlefields for a total of four battlefields. Each set contains one battlefield for the current player and another for the opposite player. One set is going to be in #p1 and the other one in #p2. Only one of the players will be in the viewport, but both retain their pointer events and their flow in the document. Here’s a little sketch:

Now we need lots of HTML. Each player needs two battlefields, which need to have 12x12 fields. That’s 576 fields in total, so we’re going to loop around a bit.

The fields are going to have their own class declaring their position in the grid. Also, fields in the first row or line get a position indicator, so you get to say something cool like "Fire at C6."

each party in 'p1', 'p2'] .mode(id=party) each faction in 'enemy', 'player'] .battlefield(class=faction, class=party) each line in 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] each col, colI in 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L'] div(class='field-x' + (colI+1) + '-y' + line) if (col === 'A') .indicator-col #{line} if (line === 1) .indicator-line #{col}

The battlefield itself is going to be set in a CSS grid, with its template and measurements coming from the variables we set before. We’ll position them absolutely within our .mode divs and switch the enemy position with the player. In the actual board game, you have your own ships on the bottom as well. Note that we need to escape the calc on the top value, or Less will try to calculate it for you and fail.

.battlefield { position: absolute; display: grid; grid-template-columns: repeat(@gridSize, 1fr); width: var(--grid-measurements); height: var(--grid-measurements); margin: 0 auto 5vw; border: 2px solid; transform: translate(-50%, 0); z-index: @zSea; &.player { top: calc(var(--grid-measurements) ~"+" 150px); border-color: transparent; :target & { border-color: @playerColor; } } &.enemy { top: 100px; border-color: transparent; :target & { border-color: @enemyColor; } } }

We want the tiles of the battlefield to be a nice checkerboard pattern. I wrote a mixin to calculate the colors, and since I like my mixins separated from the rest, this is going into a components.less file.

.checkerboard(@counter) when (@counter > 0) { .checkerboard(@counter - 2); &[class^='field-'][class$='-y@{counter}'] { &:nth-of-type(odd) { background-color: transparent; :target & { background-color: darken(@seaColor, 3%); } } &:nth-of-type(even) { background-color: transparent; :target & { background-color: darken(@seaColor, 4%); } } } }

When we call it with .checkerboard(@gridSize);, it will iterate through every second line of the grid and set background colors for odd and even instances of the current element. We can color the remaining fields with an ordinary :odd and :even.

Next, we place the indicators outside of the battlefields.

[class^='field-'] { position: relative; display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; background-color: transparent; .checkerboard(@gridSize); :target &:nth-of-type(even) { background-color: darken(@seaColor, 2%); } :target &:nth-of-type(odd) { background-color: darken(@seaColor, 1%); } [class^='indicator-'] { display: none; :target & { position: absolute; display: flex; justify-content: center; width: calc(var(--grid-measurements)~"/"@gridSize); height: calc(var(--grid-measurements)~"/"@gridSize); color: lighten(@seaColor, 10%); pointer-events: none; } &.indicator-line { top: -1.5em; align-items: flex-start; } &.indicator-col { left: -2.3em; align-items: center; } } } Implementing the ships

Let’s get to the tricky part and place some ships. Those need to be clickable and interactive, so they’re going to be checkboxes. Actually, we need two checkboxes for one ship: miss and hit.

  • Miss is the bottom one. If nothing else is on that field, your shot hits the water and triggers a miss-animation. The exception is when a player clicks on their own battlefield. In that case, the ship animation plays.
  • When an own ships spawns, it activates a new checkbox. This one is called hit. It’s placed at the exact same coordinates as its corresponding ship, but in the other players attack field and above the checkbox helper for the miss. If a hit is activated, it displays a hit animation on the current player’s attack field as well as well as on the opponent’s own ship.

This is why we need to position our battlefields absolutely next to each other. We need them aligned at all times in order to let them interact with each other.

First, we’re going to set some styles that apply to both checkboxes. We still need the pointer events, but want to visually hide the checkbox and work with helper elements instead.

.check { position: absolute; top: 0; left: 0; width: 100%; height: 100%; margin: 0; opacity: 0; + .check-helper { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; } }

We’ll also write some classes for our events for later use right now. This will also go into components.less:

.hit-obj { position: absolute; visibility: visible; left: 0; top: 0; width: 100%; height: 100%; border-radius: 50%; animation: hit 1s forwards; } .ship-obj { position: absolute; left: 0; top: 0; width: 90%; height: 90%; border-radius: 15%; animation: setShip 0.5s forwards; } .miss-obj { position: absolute; left: 0; top: 0; width: 100%; height: 100%; border-radius: 50%; animation: miss 1s forwards; } Spawning and missing ships

Those two events are basically the same. If you hit the sea in your own battlefield, you create a ship. If you hit the sea in the enemy battlefield, you trigger a miss. That happens by calling the respective class from our components.less file within a pseudo element of the helper class. We use pseudo elements here because we need to place two objects in one helper later on.

If you spawn a ship, you shouldn’t be able to un-spawn it, so we make it lose its pointer events after being checked. However, the next hit-checkbox gains it pointer events, enabling the enemy to hit spawned ships.

.check { &.ship { &:checked { pointer-events: none; } &:checked + .check-helper { :target .player & { &::after { content: ""; .ship-obj; // set own ship } } :target .enemy & { &::after { content: ""; .miss-obj; // miss enemy ship } } } &:checked ~ .hit { pointer-events: all; } } } Hitting ships

That new hit checkbox is positioned absolutely on top of the other player's attack field. For Player 1 that means by 50vw to the right and by the grid height + 50px margin to the top. It has no pointer events by default, they are going to be overwritten by those set in .ship:check ~ .hit, so only ships that are actually set, can be hit.

To display a hit event, we need two pseudo elements: one that confirms the hit on the attack field; and one that shows the victim where they have been hit. :checked + .check-helper::after calls a .hit-obj from components.less onto the attacker’s field and the corresponding ::before pseudo element gets translated back to the victim’s own battlefield.

Since the display of hit events isn’t scoped to the active player, we need to remove all unnecessary instances manually using display: none;.

.check { &.hit { position: absolute; top: ~"calc(-1 * (var(--grid-measurements) + 50px))"; left: 50vw; width: 100%; height: 100%; pointer-events: none; #p2 &, #p1:target & { left: 0; } #p1:not(:target) & + .check-helper::before { left: 50vw; } &:checked { opacity: 1; visibility: hidden; pointer-events: none; + .check-helper { &::before { content: ""; .hit-obj; // hit enemy ships top: ~"calc(-1 * (var(--grid-measurements) + 50px))"; } &::after { content: ""; .hit-obj; // hit own ships top: -2px; left: -2px; } #p1:target &::before, #p1:target ~ #p2 &::after, #p1:not(:target) &::after, #p2:target &::before { display: none; } } } #p1:target .battlefield.p1 &, #p2:target .battlefield.p2 & { display: none; } } } Animating the events

While we did style our miss, ship and hit objects, there’s nothing to be seen yet. That’s because we are still missing the animations making those objects visible. Those are simple keyframe animations that I put into a new Less file called animations.less.

@keyframes setShip { 0% { transform: scale(0, 0); background-color: transparent; } 100% { transform: scale(1, 1); background-color: @playerColor; } } @keyframes hit { 0% { transform: scale(0, 0); opacity: 0; background-color: transparent; } 10% { transform: scale(1.2, 1.2); opacity: 1; background-color: spin(@hitColor, 40); box-shadow: 0 0 0 0.5em var(--shadowColor); } 100% { transform: scale(.7, .7); opacity: .7; background-color: @hitColor; box-shadow: 0 0 0 0.5em var(--shadowColor); } } @keyframes miss { 0% { transform: scale(0, 0); opacity: 1; background-color: lighten(@seaColor, 50); } 100% { transform: scale(1, 1); opacity: .8; background-color: lighten(@seaColor, 10); } } Add customizable player names

This isn’t really necessary for functionality, but it’s a nice little extra. Instead of being called "Player 1" and "Player 2," you can enter your own name. We do this by adding two <input type="text"> to .status, one for each player. They have placeholders in case the players don’t want to enter their names and want to skip to the game right away.

.status input(type="text" placeholder="1st Player").player-name#name1 input(type="text" placeholder="2nd Player").player-name#name2 each party in ['p1', 'p2'] a.player-link.switch(href='#' + party) a.status-link.playpause(href='#pause') End Turn

Because we put them into .status, we can display them on every screen. On the start screen, we leave them as normal input fields, for the players to enter their names. We style their placeholders to look like the actual text input, so it doesn’t really matter if players enter their names or not.

.status { .player-name { position: relative; padding: 3px; border: 1px solid @enemyColor; background: transparent; color: @playerColor; &::placeholder { color: @playerColor; opacity: 1; // Reset Firefox user agent styles } } }

On the other screens, we remove their typical input field styles as well as their pointer events, making they appear as normal, non-changeable text. .status also contains empty links to select players. We style those links to have actual measurements and display the name inputs without pointer events above them. Clicking a name triggers the link now, targeting the corresponding mode.

.status { .mode#pause:target ~ & { top: 40vh; width: calc(100% ~"-" 40px); padding: 0 20px; text-align: center; z-index: @zAbove; .player-name, .player-link { position: absolute; display: block; width: 80%; max-width: 500px; height: 40px; margin: 0; padding: 0; &:nth-of-type(even) { top: 60px; } } .player-name { border: 0; text-align: center; pointer-events: none; } } }

The player screens only need to display the active player, so we remove the other one.

.status { .mode#p1:target ~ & #name2 { display: none; } .mode#p2:target ~ & #name1 { display: none; } }

Some notes on the Internet Explorer and Edge: Microsoft browsers haven’t implemented the ::placeholder pseudo element. While they do support :-ms-input-placeholder for IE and ::-ms-input-placeholder, as well as the webkit-prefix for Edge, those prefixes only work if ::placeholder is not set. As far as I played around with placeholders, I only managed to style them properly in either the Microsoft browsers, or all the other ones. If someone else has a workaround, please share it!

Putting it all together

What we have so far is a functional, but not very handsome game. I use the start screen to clarify some basic rules. Since we don’t have a hard-coded win condition and nothing to prevent players to place their ships wildly all over the place, I created a "Play fair" note that encourages the good ol' honor system.

.mode#start .battlefield.enemy ol li span You are this color. li span Your enemy is span this span color li span You may place your ships as follows: ul li 1 x 5 blocks li 2 x 4 blocks li 3 x 3 blocks li 4 x 2 blocks

I’m not going into the detail of how I went about getting things exactly to my liking since most of that is very basic CSS. You can go through the end result to pick them out.

When we finally connect all the pieces, we get this:

See the Pen CSS Game: Battleships by Daniel Schulz (@iamschulz) on CodePen.

Wrapping up

Let’s look back at what we’ve accomplished.

HTML and CSS may not be programming languages, but they are mighty tools in their own domain. We can manage states with pseudo classes and manipulate the DOM with pseudo elements.

While most of us use :hover and :focus all the time, :checked goes by largely unnoticed, only for styling actual checkboxes and radio buttons at best. Checkboxes are handy little tools that can help us to get rid of unnecessary JavaScript in our more simple front end features. I wouldn’t hesitate to build dropdown or off-canvas menus in pure CSS in real life projects, as long as the requirements don’t get too complicated.

I’d be a bit more cautious when using the :target selector. Since it uses the URL hash value, it’s only usable for a global value. I think I’d use it for, say, highlighting the current paragraph on a content page, but not for reusable elements like a slider or an accordion menu. It can also quickly get messy on larger projects, especially when other parts of it start controlling the hash value.

Building the game was a learning experience for me, dealing with pseudo selectors interacting with each other and playing around with lots of pointer events. If I had to build it again, I’d surely choose another path, which is a good outcome for me. I definitely don’t see it as a production-ready or even clean solution, and those super specific selectors are a nightmare to maintain, but it has some good parts in it that I can transition to real life projects.

Most importantly though, it was a fun thing to do.

The post Building Battleship in CSS appeared first on CSS-Tricks.

?Task management has never been easier

Thu, 08/16/2018 - 3:53am

(This is a sponsored post.)

monday.com is a team management tool that is exceptionally suitable for any industry sector and by any sized team. It will perfectly serve a team of two or a team of hundreds spread around the globe, and it can manage multiple projects at once.

monday.com promotes effortless collaboration and transparency, it's "cheetah fast," it displays status in as many as 20 different colors, and its status board can be customized to fit your needs and your workflow.

It serves as an excellent alternative to having to force fit your work to meet the demands of your project management tools or systems.

Direct Link to ArticlePermalink

The post ?Task management has never been easier appeared first on CSS-Tricks.

Understanding why Semantic HTML is important, as told by TypeScript

Wed, 08/15/2018 - 9:43am

What a great technological analogy by Mandy Michael. A reminder that TypeScript...

makes use of static typing so, for example, you can give your variables a type when you write your code and then TypeScript checks the types at compile time and will throw an error if the variable is given a value of a different type.

In other words, you have a variable age that you declare to be a number, the value for age has to stay a number otherwise TypeScript will yell at you. That type checking is a valuable thing that helps thwart bugs and keep code robust.

This is the same with HTML. If you use the <div> everywhere, you aren’t making the most of language. Because of this it’s important that you actively choose what the right element is and don’t just use the default <div>.

And hey, if you're into TypeScript, it's notable it just went 3.0.

Direct Link to ArticlePermalink

The post Understanding why Semantic HTML is important, as told by TypeScript appeared first on CSS-Tricks.

Creating the “Perfect” CSS System

Wed, 08/15/2018 - 9:19am

My pal Lindsay Grizzard wrote about creating a CSS system that works across an organization and all of the things to keep in mind when starting a new project:

Getting other developers and designers to use the standardized rules is essential. When starting a project, get developers onboard with your CSS, JS and even HTML conventions from the start. Meet early and often to discuss every library, framework, mental model, and gem you are interested in using and take feedback seriously. Simply put, if they absolutely hate BEM and refuse to write it, don’t use BEM. You can explore working around this with linters, but forcing people to use a naming convention they hate isn’t going to make your job any easier. Hopefully, you will be able to convince them why the extra underscores are useful, but finding a middle ground where everyone will participate in some type of system is the priority.

I totally agree and the important point I noted here is that all of this work is a collaborative process and compromise is vital when making a system of styles that are scalable and cohesive. In my experience, at least, it’s real easy to walk into a room with all the rules written down and new guidelines ready to be enforced, but that never works out in the end.

Ethan Marcotte riffed on Lindsay’s post in an article called Weft and described why that’s not always a successful approach:

Broad strokes here, but I feel our industry tends to invert Lindsay’s model: we often start by identifying a technical solution to a problem, before understanding its broader context. Put another way, I think we often focus on designing or building an element, without researching the other elements it should connect to—without understanding the system it lives in.

Direct Link to ArticlePermalink

The post Creating the “Perfect” CSS System appeared first on CSS-Tricks.

Practical CSS Scroll Snapping

Wed, 08/15/2018 - 4:05am

CSS scroll snapping allows you to lock the viewport to certain elements or locations after a user has finished scrolling. It’s great for building interactions like this one:

Live Demo Browser support and basic usage

Browser support for CSS scroll snapping has improved significantly since it was introduced in 2016, with Google Chrome (69+), Firefox, Edge, and Safari all supporting some version of it.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

DesktopChromeOperaFirefoxIEEdgeSafari69No6311*18*11Mobile / TabletiOS SafariOpera MobileOpera MiniAndroidAndroid ChromeAndroid Firefox11.0-11.2NoNoNoNo60

Scroll snapping is used by setting the scroll-snap-type property on a container element and the scroll-snap-align property on elements inside it. When the container element is scrolled, it will snap to the child elements you’ve defined. In its most basic form, it looks like this:

<div class='container'> <section class='child'></section> <section class='child'></section> <section class='child'></section> ... </div> .container { scroll-snap-type: y mandatory; } .child { scroll-snap-align: start; }

This is different to the first version of the spec, which allowed you to set snap-points manually using the repeat keyword:

.container { scroll-snap-points-y: repeat(300px); }

This method is pretty limited. Since it only allows evenly-spaced snap points, you can’t really build an interface that snaps to different-sized elements. If elements change their shape across different screen sizes, you’re also bound to run into issues.

At the time of this writing, Firefox, Internet Explorer, and Edge support the older version of the spec, while Chrome (69+) and Safari support the newer, element-based method.

You can use both methods alongside each other (if your layout allows it) to support both groups of browsers:

.container { scroll-snap-type: mandatory; scroll-snap-points-y: repeat(300px); scroll-snap-type: y mandatory; } .child { scroll-snap-align: start; }

I’d argue a more flexible option is to use the element-based syntax exclusively and loading a polyfill to support browsers that don’t yet support it. This is the method I’m using in the examples below.

Unfortunately the polyfill doesn't come with a browser bundle, so it's a bit tricky to use if you're not using a build process. The easiest way around this I've found is to link to the script on bundle.run and initializing it using cssScrollSnapPolyfill() once the DOM is loaded. It’s also worth pointing out that this polyfill only supports the element-based syntax, not the repeat-method.

Parent container properties

As with any property, it’s a good idea to get familiar with the values they accept. Scroll snap properties are applied to both parent and child elements, with specific values for each. Sort of the same way flexbox and grid do, where the parent becomes a "flex" or "grid" container. In this case, the parent becomes a snap container, if you will.

Here are the properties and values for the parent container and how they work.

scroll-snap-type “mandatory” vs. “proximity”

The mandatory value means the browser has to snap to a snap point whenever the user stops scrolling. The proximity property is less strict—it means the browser may snap to a snap point if it seems appropriate. In my experience this tends to kick in when you stop scrolling within a few hundred pixels of a snap point.

See the Pen Scroll-snap-type "Mandatory" vs "Proximity" by Max Kohler (@maxakohler) on CodePen.

In my own work, I’ve found that mandatory makes for a more consistent user experience, but it can also be dangerous, as the spec points out. Picture a scenario where an element inside a scrolling container is taller than the viewport:

If that container is set to scroll-snap-type: mandatory, it will always snap either to the top of the element or the top of the one below, making the middle part of the tall element impossible to scroll to.

scroll-padding

By default content will snap to the very edges of the container. You can change that by setting the scroll-padding property on the container. It follows the same syntax as the regular padding property.

This can be useful if your layout has elements that could get in the way of the content, like a fixed header.

Properties on the children

Now let’s move on over to the properties for child elements.

scroll-snap-align

This lets you specify which part of the element is supposed to snap to the container. It has three possible values: start, center, and end.

These are relative to the scroll direction. If you’re scrolling vertically, start refers to the top edge of the element. If you’re scrolling horizontally, it refers to the left edge. center and end follow the same principle. You can set a different values for each scroll direction separated by a space.

scroll-snap-stop “normal” vs. “always”

By default, scroll snapping only kicks in when the user stops scrolling, meaning they can skip over several snap points before coming to a stop.

You can change this by setting scroll-snap-stop: always on any child element. This forces the scroll container to stop on that element before the user can continue to scroll.

At the time of this writing no browser supports scroll-snap-stop natively, though there is a tracking bug for Chrome.

Let’s look at some examples of scroll snap in use.

Example 1: Vertical list

To make a vertical list snap to each list element only takes a few lines of CSS. First, we tell the container to snap along its vertical axis:

.container { scroll-snap-type: y mandatory; }

Then, we define the snap points. Here, we're specifying that the top of each list element is going to be a snap point:

.child { scroll-snap-align: start; }

See the Pen Vertical List by Max Kohler (@maxakohler) on CodePen.

Example 2: Horizontal slider

To make a horizontal slider, we tell the container to snap along its x-axis. We’re also using scroll-padding to make sure the child elements snap to the center of the container.

.container { scroll-snap-type: x mandatory; scroll-padding: 50%; }

Then, we tell the container which points to snap to. To center the gallery, we define the center point of each element as a snap point.

.child { scroll-snap-align: center; }

See the Pen Horizontal, different sized images by Max Kohler (@maxakohler) on CodePen.

Example 3: Vertical full screen

We can set the snap points directly on the <body> element:

body { scroll-snap-type: y mandatory; }

Then, we make each section the size of the viewport and define the top edge as a snap point:

section { height: 100vh; width: 100vw; scroll-snap-align: start; }

See the Pen Vertical Full-Screen by Max Kohler (@maxakohler) on CodePen.

Example 4: Horizontal full screen

This is the same sort of concept as the vertical version, but with the snap point on the x-axis instead.

body { scroll-snap-type: x mandatory; } section { height: 100vh; width: 100vw; scroll-snap-align: start; }

See the Pen Horizontal Full-Screen by Max Kohler (@maxakohler) on CodePen.

Example 5: 2D image grid

Scroll snapping can work in two directions at the same time. Again, we can set scroll-snap-type directly on the <body> element:

.container { scroll-snap-type: both mandatory; }

Then, we define the top-left corner of each tile as a snap point:

.tile { scroll-snap-align: start; }

See the Pen 2d Snapping by Max Kohler (@maxakohler) on CodePen.

Some thoughts on user experience

Messing with scrolling is risky business. Since it’s such a fundamental part of interacting with the web, changing it in any way can feel jarring—the term scrolljacking used to get thrown around to describe that sort of experience.

The great thing about CSS-based scroll snapping is that you're not taking direct control over the scroll position. Instead you're just giving the browser a list of positions to snap in a way that is appropriate to the platform, input method, and user preferences. This means a scrolling interface you build is going to feel just like the native interface (i.e using the same animations, etc.) on whatever platform it's viewed on.

To me, this is the key advantage of CSS scroll snapping over JavaScript libraries that offer similar functionality.

This works fairly well in my experience, especially on mobile. Maybe this is because scroll snapping is already part of the native UI on mobile platforms. (Picture the home screens on iOS and Android—they’re essentially horizontal sliders with snap points.) The interaction on Chrome on Android is particularly nice because it feels like regular scrolling, but the viewport always happens to come to a stop at a snap point:

There’s definitely some fancy maths going on to make this happen. Thanks to CSS scroll snapping, we’re getting it for free.

Of course, we shouldn’t start throwing snap points onto everything. Things like article pages do just fine without them. But I think they can be a nice enhancement in the right situation—image galleries, slideshows seem like good candidates, but maybe there’s potential beyond that.

Conclusion

If done thoughtfully, scroll snapping can be a useful design tool. CSS snap points allow you to hook into the browser’s native scrolling interaction, so your interface feel seamless and smooth. With a JavaScript API potentially on the horizon, these are going to become even more powerful. Still, a light touch is probably the way to go.

The post Practical CSS Scroll Snapping appeared first on CSS-Tricks.

The possibilities of the color-adjust property

Tue, 08/14/2018 - 6:22am

The Open Web continues to show up in places we would have never originally expected to find it: our phones, televisions, watches, books, video game consoles, fast food menus, gas pumps, elevators, cars—even our refrigerators.

By not making too many or too strict assumptions about how the web should be used, it remains flexible and adaptable. These qualities have allowed it to outperform closed technologies like Flash and Silverlight.

With the web’s growth comes new features to better accommodate its new form factors and use cases. One feature I’m excited about is the color-adjust property, proposed in CSS Color Module Level 4. It is an acknowledgement that the web will continue to show up on devices that have less-than-stellar displays.

There are two values for color-adjust: economy and exact. A value of exact tells the browser it shouldn’t make adjustments to the colors declared in the stylesheet:

.card { background-color: #98b3c7; border-bottom: 0.25rem solid #7c92a3; color: #f3f3f3; color-adjust: exact; ... }

The color-adjust: exact; declaration in this example forces the browser to render all colors as accurately as possible on anything with a class of card applied to it. Accurate meaning being as close as possible based on the host device’s best ability.

The description for the economy value in the specification reads as, “The user agent should make adjustments to the page’s styling as it deems necessary and prudent for the output device.” It places trust in the browser’s hands, allowing it to make adjustments to color values as it sees fit.

Best ability

Handing control over to a browser might seem a little scary at first. As an industry, we’re really great at bikeshedding the heck out of color systems. And that’s a great thing! The use of color, including proper contrast ratios, is an incredibly important aspect of design, and can oftentimes make or break a product.

But we need to understand that our platonically ideal design might not be able to be experienced in the real world as intended. Not everyone owns a device that outputs to a Retina display with a luxurious DCI-P3 color space; nor do they always have perfect vision or ideal lighting conditions. In these kinds of circumstances, it’s better to bend, not break.

We now live in a Mobile, Small, Portrait, Slow, Interlace, Monochrome, Coarse, Non-Hover, First world. Limited color displays aren’t as rare as you think, and are probably only going to get more commonplace as time goes on. I’d especially like to call attention to the rise of internet use by low income populations and emerging markets. With that comes cheaper devices with lower-quality displays.

Browser support

At the time of this article’s publishing, color-adjust has been supported since Firefox 48 (and Android Firefox 60):

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

DesktopChromeOperaFirefoxIEEdgeSafari19*15*48No186*Mobile / TabletiOS SafariOpera MobileOpera MiniAndroidAndroid ChromeAndroid Firefox12NoNo676760

Chrome and Safari, both WebKit browsers, require a vendor-prefixed declaration of -webkit-print-color-adjust. Curiously, -webkit-print signals that their implementation of this property is only intended for print. While the W3C documentation does mention use cases for printing, it is phrased in such a way as to not be limited to it.

People still print webpages! Paper doesn’t require a data plan, nor does its connection drop when you go underground. Just yesterday, I saw someone on the train who was using a collection of printed sites to study for their next exam. And here’s your galaxy brain moment: printed pages are just limited color displays.

I’d also be remiss if I didn’t mention situations where print styles are missing or poorly-authored, potentially forcing a printer to waste ink in an attempt to do what the stylesheet asks of it. Printer ink is hideously expensive—minding this does your (or your IT department’s) budget a solid.

Potential uses

Before we get any further, I want to state the following is all personal theorizing based off the phrasing in the W3C specification—targeting, but not limited to printing.

I think color-adjust could be one of those properties that could find a home explicitly declared in the body selector, where it can best take advantage of the Cascade:

body { color-adjust: economy; ... }

This declaration says, “Every time I declare color in this website, use the values I specify. If you can’t, that’s cool—do what you think is best.” That’s a lot better than the browser trying to literally interpret styling instructions at all cost, potentially rendering the page as completely illegible.

You could declare color-adjust in a more specific way, say nested in a @supports at-rule inside of a print media query, but that’s unnecessary extra work. It would fail to accommodate things like High Contrast Mode and the upcoming color gamut media feature. Better to embrace the unknown and cast a wide net.

I’m also very curious to see how color-adjust could work in conjunction with other browser capabilities, say the Ambient Light Sensor API (RIP Battery Status API). It’d be neat if there were opportunities to experiment with other specialized display modes—macOS’ Night Shift, Increased Contrast, Grayscale, and Reduce Transparency options all come to mind.

A note about accessibility

I’m wondering if software (browser preference toggle or extension, bookmarklet, etc.) could be written to override what the device’s hardware reports itself as being. Much like User Agent spoofing, it could “trick” a browser into thinking it has a limited color display, using economy to force better contrast between text and background color. This would be a lot like some browser’s reading modes, only page layout would be better preserved.

That being said, I don’t think color-adjust is a silver bullet for all color-related accessibility concerns. We can’t always know the device and context our websites and web apps will show up in, including what colors color-adjust would ultimately render as. Because of this, it’s still important to mind your color contrast ratios.

Bending, not breaking

color-adjust feels like a natural extension of Jen Simmons’ Intrinsic Web Design: fluid and squishy UI, proportional sizing, media queries as needed, and simple declarations that do the heavy lifting.

The beauty of the CSS Cascade means you can gracefully create intent, then adjust as needed. color-adjust's documentation specifically mentions a situation where it could be useful to ensure a table’s zebra striping is is retained when printed to make it easier to read. Such a tweak can be scoped to a single selector, without having to spend time undoing it for every other component.

body { color-adjust: economy; ... } tr { color-adjust: exact; }

The beauty of CSS’ fault tolerance means browsers that don’t understand this declaration will ignore it and continue parsing the rest of of the stylesheet. Browsers that do support it can take advantage of it, without any complicated build tool configuration or dangerous User Agent sniffing.

It is important to make our web sites and web apps design adapt to the user’s environment and circumstances, and not the other way around. Good user experiences meet the user where they are, not where we hope they’ll be.

The post The possibilities of the color-adjust property appeared first on CSS-Tricks.

An Event Apart

Tue, 08/14/2018 - 6:21am

Just the other day in a Slack group I hang out in someone asked "what web design events is everyone going to and loving?" An Event Apart is always my immediate answer. I've gotten to speak a number of An Event Apart events, which is an incredible honor and always a good time. So from that perspective, I love it. I can tell you that it's the most well-run conference I go to that gets all the details right.

But I've also attended An Event Apart as a paying attendee and loved that. There is something about An Event Apart that gets the speakers to really bring their A-game, and you get to watch that A-game over and over for a couple of days.

Upcoming Events in 2018: About AEA

Learn from the best: An Event Apart’s speakers aren’t merely industry leaders with years of experience and deep insight. They’re also engaging, inspiring thinkers who are passionate about sharing what they’ve learned and helping you level up your skills.

And because our editorial team works closely with those speakers, you’ll find that each presentation sheds light on the next—that the event is not merely a collection of disparate talks, but a curated, cutting-edge curriculum for the modern designer, coder, and strategist. A curriculum that continues after the conference ends, with slides, articles, videos, and other resources shared freely on our website.

At An Event Apart, you won’t just learn what’s happening right now. You’ll also look ahead to what’s next. Topics people heard about on our stage before the rest of the industry knew they were coming: responsive web design, mobile-first practices, content strategy, responsive content strategy, and, now, intrinsic web design and variable fonts.

To design a better experience, attend a better-designed UX conference.

AEA Discount Code

Make sure to use AEACP to get $100 of any 2 or 3 day event!

The post An Event Apart appeared first on CSS-Tricks.

Simple Interactive Pie Chart with CSS Variables and Houdini Magic

Mon, 08/13/2018 - 3:57am

I got the idea for doing something of the kind when I stumbled across this interactive SVG pie chart. While the SVG code is as compact as it gets (a single <circle> element!), using strokes for creating pie chart slices is problematic as we run into rendering issues on Windows for Firefox and Edge. Plus, in 2018, we can accomplish a lot more with a lot less JavaScript!

AI got the following result down to a single HTML element for the chart and very little JavaScript. The future should completely eliminate the need for any JavaScript, but more on that later.

The final pie chart result.

Some of you may remember Lea Verou's Missing Slice talk—my solution is based on her technique. This article dissects how it all works, showing what we can do in terms of graceful degradation and other ways this technique can be put to use.

The HTML

We use Pug to generate the HTML from a data object that contains unitless percentage values for the past three years:

- var data = { 2016: 20, 2017: 26, 2018: 29 }

We make all our elements reside in a .wrap element. Then, we loop through our data object and, for each of its properties, we create a radio input with a corresponding label. After these, we add a .pie element to the mix.

- var darr = [], val; .wrap - for(var p in data) { - if(!val) val = data[p]; input(id=`o${p}` name='o' type='radio' checked=val == data[p]) label(for=`o${p}`) #{p} - darr.push(`${data[p]}% for year ${p}`) - } .pie(aria-label=`Value as pie chart. ${darr.join(', ')}.` role='graphics-document group')

The above Pug code compiles to the following HTML:

<style><div class="wrap"> <input id="o2016" name="o" type="radio" checked="checked"/> <label for="o2016">2016</label> <input id="o2017" name="o" type="radio"/> <label for="o2017">2017</label> <input id="o2018" name="o" type="radio"/> <label for="o2018">2018</label> <div class="pie" aria-label="Value as pie chart. 20% for year 2016, 26% for year 2017, 29% for year 2018." role="graphics-document group"></div> </div>

Note that we also made sure only the first radio input is checked.

Passing custom properties to the CSS

I normally don't like putting styles in HTML but, in this particular case, it's a very useful way to pass custom property values to the CSS and ensure that we only need to update things in one place if we need to change any of our data points—the Pug code. The CSS remains the same.

The trick is to set a unitless percentage --p on the .pie element for every radio input that might be checked:

style - for(var p in data) { | #o#{p}:checked ~ .pie { --p: #{data[p]} } - }

We use this percentage for a conic-gradient() on the .pie element after making sure neither of its dimensions (including border and padding) are 0:

$d: 20rem; .wrap { width: $d; } .pie { padding: 50%; background: conic-gradient(#ab3e5b calc(var(--p)*1%), #ef746f 0%); }

Note that this requires native conic-gradient() support since the polyfill doesn't work with CSS variables. At the moment, this limits support to Blink browsers with the Experimental Web Platform features flag enabled, though things are bound to get better.

The Experimental Web Platform features flag enabled in Chrome.

We now have a working skeleton of our demo—picking a different year via the radio buttons results in a different conic-gradient()!

The basic functionality we've been after (live demo, Blink browsers with flag enabled only). Displaying the value

The next step is to actually display the current value and we do this via a pseudo-element. Unfortunately, number-valued CSS variables cannot be used for the value of the content property, so we get around this by using the counter() hack.

.pie:after { counter-reset: p var(--p); content: counter(p) '%'; }

We've also tweaked the color and font-size properties so that our pseudo-element is a bit more visible:

Displaying the value on the chart (live demo, Blink browsers with flag enabled only). Smoothing things out

We don't want abrupt changes between values, so we smooth things out with the help of a CSS transition. Before we can transition or animate the --p variable, we need to register it in JavaScript:

CSS.registerProperty({ name: '--p', syntax: '<integer>', initialValue: 0, inherits: true });

Note that using <number> instead of <integer> causes the displayed value to go to 0 during the transition as our counter needs an integer. Thanks to Lea Verou for helping me figure this out!

Also note that explicitly setting inherits is mandatory. This wasn't the case until recently.

This is all the JavaScript we need for this demo and, in the future, we shouldn't even need this much as we'll be able to register custom properties from the CSS.

With that out of the way, we can add a transition on our .pie element.

.pie { /* same styles as before */ transition: --p .5s; }

And that's it for the functionality! All done with one element, one custom variable, and a sprinkle of Houdini magic!

Interactive pie chart (live demo, Blink browsers with flag enabled only). Prettifying touches

While our demo is functional, it looks anything but pretty at this point. So, let's take care of that while we're at it!

Making the pie... a pie!

Since the presence of :after has increased the height of its .pie parent, we absolutely position it. And since we want our .pie element to look more like an actual pie, we make it round with border-radius: 50%.

Rounding up our pie (live demo, Blink browsers with flag enabled only).

We also want to display the percentage value in the middle of the dark pie slice.

In order to do this, we first position it dead in the middle of the .pie element. By default, the :after pseudo-element is displayed after its parent's content. Since .pie has no content in this case, the top-left corner of the :after pseudo-element is in the top-left corner of the parent's content-box. Here, the content-box is a 0x0 box in the center of the padding-box. Remember that we've set the padding of .pie to 50%—a value that's relative to the wrapper width for both the horizontal and the vertical direction!

This means the top-left corner of :after is in the middle of its parent, so a translate(-50%, -50%) on it shifts it to the left by half its own width and up by half its own height, making its own mid-point coincide with that of .pie.

Remember that %-valued translations are relative to the dimensions of the element they're applied on along the corresponding axis. In other words, a %-valued translation along the x-axis is relative to the element's width, a %-valued translation along the y-axis is relative to its height and a %-valued translation along the z-axis is relative to its depth, which is always 0 because all elements are flat two-dimensional boxes with 0 depth along the third axis.

Positioning the value label in the middle of the pie (live demo, Blink browsers with flag enabled only).

Next, we rotate the value such that the positive half of its x-axis splits the dark slice into two equal halves and then translate it by half a pie radius along this now-rotated x-axis.

See the Pen by thebabydino (@thebabydino) on CodePen.

What we need to figure out is how much to rotate the :after pseudo-element so that its x-axis splits the dark slice into two equal halves. Let's break that down!

Initially, the x-axis is horizontal, pointing towards the right, so in order to have it in the desired direction, we first need to rotate it so that it points up and going along the starting edge of the slice. Then it needs to rotate clockwise by half a slice.

In order to get the axis to point up, we need to rotate it by -90deg. The minus sign is due to the fact that positive values follow a clockwise direction and we're going the other way.

See the Pen by thebabydino (@thebabydino) on CodePen.

Next, we need to rotate it by half a slice.

See the Pen by thebabydino (@thebabydino) on CodePen.

But how much is half a slice?

Well, we already know what percentage of the pie this slice represents: it's our custom property, --p. If we divide that value by 100 and then multiply it by 360deg (or 1turn, it doesn't matter what unit is used), we get the central angle of our dark slice.

After the -90deg rotation, we need to rotate :after by half this central angle in the clockwise (positive) direction.

This means we apply the following transform chain:

translate(-50%, -50%) rotate(calc(.5*var(--p)/100*1turn - 90deg)) translate(.25*$d);

The last translation is by a quarter of $d, which is the wrapper width and gives us the .pie diameter as well. (Since the content-box of .pie is a 0x0 box, it has no border and both its left and right padding are 50% of its wrapper parent width.) The .pie radius is half its diameter, meaning that half the radius is a quarter of the diameter ($d).

Now the value label is positioned where we want it to be:

Positioning the value label in the middle of the slice (live demo, Blink browsers with flag enabled only).

However, there's still one problem: we don't want it to be rotated, as that can look really awkward and neck-bending at certain angles. In order to fix this, we revert the rotation at the end. To make things easier for ourselves, we store the rotation angle in a CSS variable that we'll call --a:

--a: calc(.5*var(--p)/100*1turn - 90deg); transform: translate(-50%, -50%) rotate(var(--a)) translate(.25*$d) rotate(calc(-1*var(--a)));

Much better!

Positioning the value label in the middle of the slice, now horizontal (live demo, Blink browsers with flag enabled only). Layout

We want to have the whole assembly in the middle of the screen, so we solve this with a neat little grid trick:

body { display: grid; place-items: center center; margin: 0; min-height: 100vh }

Alright, this puts the entire .wrap element in the middle:

Positioning the whole assembly in the middle (live demo, Blink browsers with flag enabled only).

The next step is to place the pie chart above the radio buttons. We do this with a flexbox layout on the .wrap element:

.wrap { display: flex; flex-wrap: wrap-reverse; justify-content: center; width: $d; } Placing the pie chart above the radio buttons (live demo, Blink browsers with flag enabled only). Styling the radio buttons

...or more accurately, we're styling the radio button labels because the first thing that we do is hide the radio inputs:

[type='radio'] { position: absolute; left: -100vw; } After hiding the radio buttons (live demo, Blink browsers with flag enabled only).

Since this leaves us with some very ugly labels that are very hard to distinguish from one another, let's give each one some margin and padding so they don't look so crammed together, plus backgrounds so that their clickable areas are clearly highlighted. We can even add box and text shadows for some 3D effects. And, of course, we can create a separate case for when their corresponding inputs are :checked.

$c: #ecf081 #b3cc57; [type='radio'] { /* same as before */ + label { margin: 3em .5em .5em; padding: .25em .5em; border-radius: 5px; box-shadow: 1px 1px nth($c, 2); background: nth($c, 1); font-size: 1.25em; text-shadow: 1px 1px #fff; cursor: pointer; } &:checked { + label { box-shadow: inset -1px -1px nth($c, 1); background: nth($c, 2); color: #fff; text-shadow: 1px 1px #000; } } }

We've also blown up the font-size a bit and set a border-radius to smooth out the corners:

After styling the radio button labels (live demo, Blink browsers with flag enabled only). Final prettifying touches

Let's set a background on the body, tweak the font of the whole thing and add a transition for the radio labels:

The final pie chart result (live demo, Blink browsers with flag enabled only). Graceful degradation

While our demo now looks good in Blink browsers with the flag enabled, it looks awful in all other browsers...and that's most browsers!

First off, let's put our work inside a @supports block that checks for native conic-gradient() support so the browsers that support it will render the pie chart. This includes our conic-gradient(), the padding that gives the pie equal horizontal and vertical dimensions, the border-radius that makes the pie circular, and the transform chain that positions the value label in the middle of the pie slice.

.pie { @supports (background: conic-gradient(tan, red)) { padding: 50%; border-radius: 50%; background: conic-gradient(var(--stop-list)); --a: calc(.5*var(--p)/100*1turn - 90deg); --pos: rotate(var(--a)) translate(#{.25*$d}) rotate(calc(-1*var(--a))); } } }

Now, let's construct a bar chart as a fallback for all other browsers using linear-gradient(). We want our bar to stretch across the .wrap element so that the horizontal padding is still 50%, but vertically as a narrow bar. We still want the chart to be tall enough to fit the value label. This means we will go with smaller vertical padding. We will also decrease the border-radius, since 50% would give us an ellipse and what we need is a rectangle with slightly rounded corners.

The fallback will also replace conic-gradient() with a left-to-right linear-gradient(). Since both the linear-gradient() creating the fallback bar chart and the conic-gradient() creating the pie chart use the same stop list, we can store it in a CSS variable (--stop-list) so that we don't even have it repeated in the compiled CSS.

Finally, we want the value label in the middle of the bar for the fallback since we don't have pie slices anymore. This means we store all the post-centering positioning into a CSS variable (--pos) whose value is nothing in the no conic-gradient() support case and the transform chain:

.pie { padding: 1.5em 50%; border-radius: 5px; --stop-list: #ab3e5b calc(var(--p)*1%), #ef746f 0%; background: linear-gradient(90deg, var(--stop-list)); /* same as before */ &:after { transform: translate(-50%, -50%) var(--pos, #{unquote(' ')}); /* same as before */ } @supports (background: conic-gradient(tan, red)) { padding: 50%; border-radius: 50%; background: conic-gradient(var(--stop-list)); --a: calc(.5*var(--p)/100*1turn - 90deg); --pos: rotate(var(--a)) translate(#{.25*$d}) rotate(calc(-1*var(--a))); } } }

We also switch to using a flexbox layout on the body (since, as clever as it may be, the grid one is messed up in Edge).

body { display: flex; align-items: center; justify-content: center; /* same as before */ }

This all gives us a bar chart fallback for the browsers not supporting conic-gradient().

The fallback for the final pie chart result (live demo). Responsifying it all

The one problem we still have is that, if the viewport is narrower than the pie diameter, things don't look so good anymore.

CSS variables and media queries to the rescue!

We set the diameter to a CSS variable (--d) that gets used to set the pie dimensions and the position of the value label in the middle of our slice.

.wrap { --d: #{$d}; width: var(--d); /* same as before */ @media (max-width: $d) { --d: 95vw } }

Below certain viewport widths, we also decrease the font-size, the margin for our <label> elements, and we don't position the value label in the middle of the dark pie slice anymore, but rather in the middle of the pie itself:

.wrap { /* same as before */ @media (max-width: 265px) { font-size: .75em; } } [type='radio'] { /* same as before */ + label { /* same as before */ @media (max-width: 195px) { margin-top: .25em; } } } .pie{ /* same as before */ @media (max-width: 160px) { --pos: #{unquote(' ')} } }

This gives us our final result: a responsive pie chart in browsers supporting conic-gradient() natively. And, even though that's sadly just Blink browsers with the Experimental Web Platform features flag enabled for now, we have a solid fallback that renders a responsive bar chart for all other browsers. We also animate between values—again, that's just Blink browsers with the Experimental Web Platform features flag enabled at this point.

See the Pen by thebabydino (@thebabydino) on CodePen.

Bonus: radial progress!

We can also apply this concept to build a radial progress indicator like the one below (inspired by this Pen):

The radial progress and its fallback.

The technique is pretty much the same, except we leave the value label dead in the middle and set the conic-gradient() on the :before pseudo-element. This is because we use a mask to get rid of everything, except for a thin outer ring and, if we were to set the conic-gradient() and the mask on the element itself, then the mask would also hide the value label inside and we want that visible.

On clicking the <button>, a new value for our unitless percentage (--p) is randomly generated and we transition smoothly between values. Setting a fixed transition-duration would create a really slow transition between two close values (e.g. 47% to 49%) and a really fast transition when moving between values with a larger gap in between (e.g. 3% to 98%). We get around this by making the transition-duration depend on the absolute value of the difference between the previous value of --p and its newly generated value.

[id='out'] { /* radial progress element */ transition: --p calc(var(--dp, 1)*1s) ease-out; } const _GEN = document.getElementById('gen'), _OUT = document.getElementById('out'); _GEN.addEventListener('click', e => { let old_perc = ~~_OUT.style.getPropertyValue('--p'), new_perc = Math.round(100*Math.random()); _OUT.style.setProperty('--p', new_perc); _OUT.style.setProperty('--dp', .01*Math.abs(old_perc - new_perc)); _OUT.setAttribute('aria-label', `Graphical representation of generated percentage: ${new_perc}% of 100%.`) }, false);

This gives us a nice animated radial progress indicator for browsers supporting all the new and shiny features. We have a linear fallback for other browsers:

See the Pen by thebabydino (@thebabydino) on CodePen.

The post Simple Interactive Pie Chart with CSS Variables and Houdini Magic appeared first on CSS-Tricks.

The Cost of JavaScript in 2018

Fri, 08/10/2018 - 5:40am

Even though we mentioned it earlier, I thought this outstanding post by Addy Osmani all about the performance concerns of JavaScript was still worth digging into a little more.

In that post, Addy touches on all aspects of perf work and how we can fix some of the most egregious issues, from setting up a budget to “Time-to-Interactive” measurements and auditing your JavaScript bundles.

Embrace performance budgets and learn to live within them. For mobile, aim for a JS budget of < 170KB minified/compressed. Uncompressed this is still ~0.7MB of code. Budgets are critical to success, however, they can’t magically fix perf in isolation. Team culture, structure and enforcement matter. Building without a budget invites performance regressions and failure.

Super specific and super practical!

Surprisingly, Addy mentions that “the median webpage today currently ships about 350KB of minified and compressed JavaScript,” which seems like an awful lot lower than I’d expected, if I'm being honest. The stat that scares me most is that the median webpage takes around fifteen whole seconds until it’s interactive. And pulling all that JS into a Web Worker or caching with Service Workers won't even make up that time to interaction. Yikes.

Another key point: not all bytes are equal. For example, 200KB of JavaScript is not equal to a 200KB JPG image file:

A JPEG image needs to be decoded, rasterized, and painted on the screen. A JavaScript bundle needs to be downloaded and then parsed, compiled, executed —and there are a number of other steps that an engine needs to complete. Just be aware that these costs are not quite equivalent.

Direct Link to ArticlePermalink

The post The Cost of JavaScript in 2018 appeared first on CSS-Tricks.

The Cost of JavaScript in 2018

Fri, 08/10/2018 - 5:40am

Even though we mentioned it earlier, I thought this outstanding post by Addy Osmani all about the performance concerns of JavaScript was still worth digging into a little more.

In that post, Addy touches on all aspects of perf work and how we can fix some of the most egregious issues, from setting up a budget to “Time-to-Interactive” measurements and auditing your JavaScript bundles.

Embrace performance budgets and learn to live within them. For mobile, aim for a JS budget of < 170KB minified/compressed. Uncompressed this is still ~0.7MB of code. Budgets are critical to success, however, they can’t magically fix perf in isolation. Team culture, structure and enforcement matter. Building without a budget invites performance regressions and failure.

Super specific and super practical!

Surprisingly, Addy mentions that “the median webpage today currently ships about 350KB of minified and compressed JavaScript,” which seems like an awful lot lower than I’d expected, if I'm being honest. The stat that scares me most is that the median webpage takes around fifteen whole seconds until it’s interactive. And pulling all that JS into a Web Worker or caching with Service Workers won't even make up that time to interaction. Yikes.

Another key point: not all bytes are equal. For example, 200KB of JavaScript is not equal to a 200KB JPG image file:

A JPEG image needs to be decoded, rasterized, and painted on the screen. A JavaScript bundle needs to be downloaded and then parsed, compiled, executed —and there are a number of other steps that an engine needs to complete. Just be aware that these costs are not quite equivalent.

Direct Link to ArticlePermalink

The post The Cost of JavaScript in 2018 appeared first on CSS-Tricks.

Switch font color for different backgrounds with CSS

Fri, 08/10/2018 - 3:58am

Ever get one of those, "I can do that with CSS!" moments while watching someone flex their JavaScript muscles? That’s exactly the feeling I got while watching Dag-Inge Aas & Ida Aalen talk at CSSconf EU 2018.

They are based in Norway, where WCAG accessibility is not a just good practice, but actually required by law (go, Norway!). As they were developing a feature that allows user-selectable color theming for their main product, they faced a challenge: automatically adjusting the font color based on the selected background color of the container. If the background is dark, then it would be ideal to have a white text to keep it WCAG contrast compliant. But what happens if a light background color is selected instead? The text is both illegible and fails accessibility.

They used an elegant approach and solved the issue using the "color" npm package, adding conditional borders and automatic secondary color generation while they were at it.

But that’s a JavaScript solution. Here’s my pure CSS alternative.

The challenge

Here is the criteria I set out to accomplish:

  • Change the font color to either black or white depending on the background color
  • Apply the same sort of logic to borders, using a darker variation of the base color of the background to improve button visibility, only if background is really light
  • Automatically generate a secondary, 60º hue-rotated color
Working with HSL colors and CSS variables

The easiest approach I could think for this implies running HSL colors. Setting the background declarations as HSL where each parameter is a CSS custom property allows for a really simple way to determine lightness and use it as a base for our conditional statements.

:root { --hue: 220; --sat: 100; --light: 81; } .btn { background: hsl(var(--hue), calc(var(--sat) * 1%), calc(var(--light) * 1%)); }

This should allow us to swap the background to any color we’d like at runtime by changing the variables and running an if/else statement to change the foreground color.

Except... we don’t have if/else statements on CSS… or do we?

Introducing CSS conditional statements

Since the introduction of CSS variables, we also got conditional statements to go with them. Or sort of.

This trick is based on the fact that some CSS parameters get capped to a min and max value. For instance, think opacity. The valid range is 0 to 1, so we normally keep it there. But we can also declare an opacity of 2, 3, or 1000, and it will be capped to 1 and interpreted as such. In a similar fashion, we can even declare a negative opacity value, and get it capped to 0.

.something { opacity: -2; /* resolves to 0, transparent */ opacity: -1; /* resolves to 0, transparent */ opacity: 2; /*resolves to 1, fully opaque */ opacity: 100; /* resolves to 1, fully opaque */ } Applying the trick to our font color declaration

The lightness parameter of an HSL color declaration behaves in a similar way, capping any negative value to 0 (which results in black, whatever the hue and saturation happens to be) and anything above 100% is capped to 100% (which is always white).

So, we can declare the color as HSL, subtract the desired threshold from the lightness parameter, then multiply by 100% to force it to overshoot one of the limits (either sub-zero or higher than 100%). Since we need negative results to resolve in white and positive results to resolve in black, we also have to invert it multiplying the result by -1.

:root { --light: 80; /* the threshold at which colors are considered "light." Range: integers from 0 to 100, recommended 50 - 70 */ --threshold: 60; } .btn { /* Any lightness value below the threshold will result in white, any above will result in black */ --switch: calc((var(--light) - var(--threshold)) * -100%); color: hsl(0, 0%, var(--switch)); }

Let’s review that bit of code: starting from a lightness of 80 and considering a 60 threshold, the subtraction results in 20, which multiplied by -100%, results in -2000% capped to 0%. Our background is lighter than the threshold, so we consider it light and apply black text.

If we had set the --light variable as 20, the subtraction would have resulted in -40, which multiplied by -100% would turn 4000%, capped to 100%. Our light variable is lower than the threshold, therefore we consider it a "dark" background and apply white text to keep a high contrast.

Generating a conditional border

When the background of an element becomes too light, it can get easily lost against a white background. We might have a button and not even notice it. To provide a better UI on really light colors, we can set a border based on the very same background color, only darker.

A light background with a dark border based on that background color.

To achieve that, we can use the same technique, but apply it to the alpha channel of a HSLA declaration. That way, we can adjust the color as needed, then have either fully transparent or fully opaque.

:root { /* (...) */ --light: 85; --border-threshold: 80; } .btn { /* sets the border-color as a 30% darker shade of the same color*/ --border-light: calc(var(--light) * 0.7%); --border-alpha: calc((var(--light) - var(--border-threshold)) * 10); border: .1em solid hsla(var(--hue), calc(var(--sat) * 1%), var(--border-light), var(--border-alpha)); }

Assuming a hue of 0 and saturation at 100%, the above code will provide a fully opaque, pure red border at 70% the original lightness if the background lightness is higher than 80, or a fully transparent border (and therefore, no border at all) if it’s darker.

Setting the secondary, 60º hue-rotated color

Probably the simplest of the challenges. There are two possible approaches for it:

  1. filter: hue-rotate(60): This is the first that comes to mind, but it’s not the best solution, as it would affect the color of the child elements. If necessary, it can be reversed with an opposite rotation.
  2. HSL hue + 60: The other option is getting our hue variable and adding 60 to it. Since the hue parameter doesn’t have that capping behavior at 360 but instead wraps around (as any CSS <angle> type does), it should work without any issues. Think 400deg=40deg, 480deg=120deg, etc.

Considering this, we can add a modifier class for our secondary-colored elements that adds 60 to the hue value. Since self-modifying variables are not available in CSS (i.e. there’s no such thing as --hue: calc(var(--hue) + 60) ), we can add a new auxiliary variable for our hue manipulation to our base style and use it in the background and border declaration.

.btn { /* (...) */ --h: var(--hue); background: hsl(var(--h), calc(var(--sat) * 1%), calc(var(--light) * 1%)); border:.1em solid hsla(var(--h), calc(var(--sat) * 1%), var(--border-light), var(--border-alpha)); }

Then re-declare its value in our modifier:

.btn--secondary { --h: calc(var(--hue) + 60); }

Best thing about this approach is that it’ll automatically adapt all our calculations to the new hue and apply them to the properties, because of CSS custom properties scoping.

And there we are. Putting it all together, here’s my pure CSS solution to all three challenges. This should work like a charm and save us from including an external JavaScript library.

See the Pen CSS Automatic switch font color depending on element background.... FAIL by Facundo Corradini (@facundocorradini) on CodePen.

Except it doesn’t. Some hues get really problematic (particularly yellows and cyans), as they are displayed way brighter than others (e.g. reds and blues) despite having the same lightness value. In consequence, some colors are treated as dark and given white text despite being extremely bright.

What in the name of CSS is going on?

Introducing perceived lightness

I’m sure many of you might have noticed it way ahead, but for the rest of us, turns out the lightness we perceive is not the same as the HSL lightness. Luckily, we have some methods to weigh in the hue lightness and adapt our code so it responds to hue as well.

To do that, we need to take into consideration the perceived lightness of the three primary colors by giving each a coefficient corresponding to how light or dark the human eye perceives it. This is normally referred to as luma.

There are several methods to achieve this. Some are better than others in specific cases, but not one is 100% perfect. So, I selected the two most popular, which are good enough:

  • sRGB Luma (ITU Rec. 709): L = (red * 0.2126 + green * 0.7152 + blue * 0.0722) / 255
  • W3C method (working draft): L = (red * 0.299 + green * 0.587 + blue * 0.114) / 255
Implementing luma-corrected calculations

The first obvious implication from going with a luma-corrected approach is that we cannot use HSL, since CSS doesn’t have native methods to access the RGB values of an HSL declaration.

So, we need to switch to an RBG declaration for the backgrounds, calculate the luma from whatever method we choose, and use it on our foreground color declaration, which can (and will) still be HSL.

:root { /* theme color variables to use in RGB declarations */ --red: 200; --green: 60; --blue: 255; /* the threshold at which colors are considered "light". Range: decimals from 0 to 1, recommended 0.5 - 0.6 */ --threshold: 0.5; /* the threshold at which a darker border will be applied. Range: decimals from 0 to 1, recommended 0.8+ */ --border-threshold: 0.8; } .btn { /* sets the background for the base class */ background: rgb(var(--red), var(--green), var(--blue)); /* calculates perceived lightness using the sRGB Luma method Luma = (red * 0.2126 + green * 0.7152 + blue * 0.0722) / 255 */ --r: calc(var(--red) * 0.2126); --g: calc(var(--green) * 0.7152); --b: calc(var(--blue) * 0.0722); --sum: calc(var(--r) + var(--g) + var(--b)); --perceived-lightness: calc(var(--sum) / 255); /* shows either white or black color depending on perceived darkness */ color: hsl(0, 0%, calc((var(--perceived-lightness) - var(--threshold)) * -10000000%)); }

For the conditional borders, we need to turn the declaration into a RGBA, and once again, use the alpha channel to make it either fully transparent or fully opaque. Pretty much the same thing as before, only running RGBA instead of HSLA. The darker shade is obtained by halving each color channel.

.btn { /* (...) */ /* applies a darker border if the lightness is higher than the border threshold */ --border-alpha: calc((var(--perceived-lightness) - var(--border-threshold)) * 100); border: .2em solid rgba(calc(var(--red) * 0.5), calc(var(--green) * 0.5), calc(var(--blue) * 0.5), var(--border-alpha)); }

Since we lost our initial HSL background declaration, our secondary theme color needs to be obtained via hue rotation:

.btn--secondary { filter: hue-rotate(60deg); }

This is not the best thing in the world. Besides applying the hue rotation to potential child elements as previously discussed, it means the switch to black/white and border visibility on the secondary element will depend on the main element’s hue and not on its own. But as far as I can see, the JavaScript implementation has the same issue, so I’ll call it close enough.

And there we have it, this time for good.

See the Pen CSS Automatic WCAG contrast font-color depending on element background by Facundo Corradini (@facundocorradini) on CodePen.

A pure CSS solution that achieves the same effect as the original JavaScript approach, but significantly cuts on the footprint.

Browser support

IE is excluded due to the use of CSS variables. Edge doesn’t have that capping behavior we used throughout. It sees the declaration, deems it nonsense, and discards it altogether as it would any broken/unknown rule. Every other major browser should work.

The post Switch font color for different backgrounds with CSS appeared first on CSS-Tricks.

Switch font color for different backgrounds with CSS

Fri, 08/10/2018 - 3:58am

Ever get one of those, "I can do that with CSS!" moments while watching someone flex their JavaScript muscles? That’s exactly the feeling I got while watching Dag-Inge Aas & Ida Aalen talk at CSSconf EU 2018.

They are based in Norway, where WCAG accessibility is not a just good practice, but actually required by law (go, Norway!). As they were developing a feature that allows user-selectable color theming for their main product, they faced a challenge: automatically adjusting the font color based on the selected background color of the container. If the background is dark, then it would be ideal to have a white text to keep it WCAG contrast compliant. But what happens if a light background color is selected instead? The text is both illegible and fails accessibility.

They used an elegant approach and solved the issue using the "color" npm package, adding conditional borders and automatic secondary color generation while they were at it.

But that’s a JavaScript solution. Here’s my pure CSS alternative.

The challenge

Here is the criteria I set out to accomplish:

  • Change the font color to either black or white depending on the background color
  • Apply the same sort of logic to borders, using a darker variation of the base color of the background to improve button visibility, only if background is really light
  • Automatically generate a secondary, 60º hue-rotated color
Working with HSL colors and CSS variables

The easiest approach I could think for this implies running HSL colors. Setting the background declarations as HSL where each parameter is a CSS custom property allows for a really simple way to determine lightness and use it as a base for our conditional statements.

:root { --hue: 220; --sat: 100; --light: 81; } .btn { background: hsl(var(--hue), calc(var(--sat) * 1%), calc(var(--light) * 1%)); }

This should allow us to swap the background to any color we’d like at runtime by changing the variables and running an if/else statement to change the foreground color.

Except... we don’t have if/else statements on CSS… or do we?

Introducing CSS conditional statements

Since the introduction of CSS variables, we also got conditional statements to go with them. Or sort of.

This trick is based on the fact that some CSS parameters get capped to a min and max value. For instance, think opacity. The valid range is 0 to 1, so we normally keep it there. But we can also declare an opacity of 2, 3, or 1000, and it will be capped to 1 and interpreted as such. In a similar fashion, we can even declare a negative opacity value, and get it capped to 0.

.something { opacity: -2; /* resolves to 0, transparent */ opacity: -1; /* resolves to 0, transparent */ opacity: 2; /*resolves to 1, fully opaque */ opacity: 100; /* resolves to 1, fully opaque */ } Applying the trick to our font color declaration

The lightness parameter of an HSL color declaration behaves in a similar way, capping any negative value to 0 (which results in black, whatever the hue and saturation happens to be) and anything above 100% is capped to 100% (which is always white).

So, we can declare the color as HSL, subtract the desired threshold from the lightness parameter, then multiply by 100% to force it to overshoot one of the limits (either sub-zero or higher than 100%). Since we need negative results to resolve in white and positive results to resolve in black, we also have to invert it multiplying the result by -1.

:root { --light: 80; /* the threshold at which colors are considered "light." Range: integers from 0 to 100, recommended 50 - 70 */ --threshold: 60; } .btn { /* Any lightness value below the threshold will result in white, any above will result in black */ --switch: calc((var(--light) - var(--threshold)) * -100%); color: hsl(0, 0%, var(--switch)); }

Let’s review that bit of code: starting from a lightness of 80 and considering a 60 threshold, the subtraction results in 20, which multiplied by -100%, results in -2000% capped to 0%. Our background is lighter than the threshold, so we consider it light and apply black text.

If we had set the --light variable as 20, the subtraction would have resulted in -40, which multiplied by -100% would turn 4000%, capped to 100%. Our light variable is lower than the threshold, therefore we consider it a "dark" background and apply white text to keep a high contrast.

Generating a conditional border

When the background of an element becomes too light, it can get easily lost against a white background. We might have a button and not even notice it. To provide a better UI on really light colors, we can set a border based on the very same background color, only darker.

A light background with a dark border based on that background color.

To achieve that, we can use the same technique, but apply it to the alpha channel of a HSLA declaration. That way, we can adjust the color as needed, then have either fully transparent or fully opaque.

:root { /* (...) */ --light: 85; --border-threshold: 80; } .btn { /* sets the border-color as a 30% darker shade of the same color*/ --border-light: calc(var(--light) * 0.7%); --border-alpha: calc((var(--light) - var(--border-threshold)) * 10); border: .1em solid hsla(var(--hue), calc(var(--sat) * 1%), var(--border-light), var(--border-alpha)); }

Assuming a hue of 0 and saturation at 100%, the above code will provide a fully opaque, pure red border at 70% the original lightness if the background lightness is higher than 80, or a fully transparent border (and therefore, no border at all) if it’s darker.

Setting the secondary, 60º hue-rotated color

Probably the simplest of the challenges. There are two possible approaches for it:

  1. filter: hue-rotate(60): This is the first that comes to mind, but it’s not the best solution, as it would affect the color of the child elements. If necessary, it can be reversed with an opposite rotation.
  2. HSL hue + 60: The other option is getting our hue variable and adding 60 to it. Since the hue parameter doesn’t have that capping behavior at 360 but instead wraps around (as any CSS <angle> type does), it should work without any issues. Think 400deg=40deg, 480deg=120deg, etc.

Considering this, we can add a modifier class for our secondary-colored elements that adds 60 to the hue value. Since self-modifying variables are not available in CSS (i.e. there’s no such thing as --hue: calc(var(--hue) + 60) ), we can add a new auxiliary variable for our hue manipulation to our base style and use it in the background and border declaration.

.btn { /* (...) */ --h: var(--hue); background: hsl(var(--h), calc(var(--sat) * 1%), calc(var(--light) * 1%)); border:.1em solid hsla(var(--h), calc(var(--sat) * 1%), var(--border-light), var(--border-alpha)); }

Then re-declare its value in our modifier:

.btn--secondary { --h: calc(var(--hue) + 60); }

Best thing about this approach is that it’ll automatically adapt all our calculations to the new hue and apply them to the properties, because of CSS custom properties scoping.

And there we are. Putting it all together, here’s my pure CSS solution to all three challenges. This should work like a charm and save us from including an external JavaScript library.

See the Pen CSS Automatic switch font color depending on element background.... FAIL by Facundo Corradini (@facundocorradini) on CodePen.

Except it doesn’t. Some hues get really problematic (particularly yellows and cyans), as they are displayed way brighter than others (e.g. reds and blues) despite having the same lightness value. In consequence, some colors are treated as dark and given white text despite being extremely bright.

What in the name of CSS is going on?

Introducing perceived lightness

I’m sure many of you might have noticed it way ahead, but for the rest of us, turns out the lightness we perceive is not the same as the HSL lightness. Luckily, we have some methods to weigh in the hue lightness and adapt our code so it responds to hue as well.

To do that, we need to take into consideration the perceived lightness of the three primary colors by giving each a coefficient corresponding to how light or dark the human eye perceives it. This is normally referred to as luma.

There are several methods to achieve this. Some are better than others in specific cases, but not one is 100% perfect. So, I selected the two most popular, which are good enough:

  • sRGB Luma (ITU Rec. 709): L = (red * 0.2126 + green * 0.7152 + blue * 0.0722) / 255
  • W3C method (working draft): L = (red * 0.299 + green * 0.587 + blue * 0.114) / 255
Implementing luma-corrected calculations

The first obvious implication from going with a luma-corrected approach is that we cannot use HSL, since CSS doesn’t have native methods to access the RGB values of an HSL declaration.

So, we need to switch to an RBG declaration for the backgrounds, calculate the luma from whatever method we choose, and use it on our foreground color declaration, which can (and will) still be HSL.

:root { /* theme color variables to use in RGB declarations */ --red: 200; --green: 60; --blue: 255; /* the threshold at which colors are considered "light". Range: decimals from 0 to 1, recommended 0.5 - 0.6 */ --threshold: 0.5; /* the threshold at which a darker border will be applied. Range: decimals from 0 to 1, recommended 0.8+ */ --border-threshold: 0.8; } .btn { /* sets the background for the base class */ background: rgb(var(--red), var(--green), var(--blue)); /* calculates perceived lightness using the sRGB Luma method Luma = (red * 0.2126 + green * 0.7152 + blue * 0.0722) / 255 */ --r: calc(var(--red) * 0.2126); --g: calc(var(--green) * 0.7152); --b: calc(var(--blue) * 0.0722); --sum: calc(var(--r) + var(--g) + var(--b)); --perceived-lightness: calc(var(--sum) / 255); /* shows either white or black color depending on perceived darkness */ color: hsl(0, 0%, calc((var(--perceived-lightness) - var(--threshold)) * -10000000%)); }

For the conditional borders, we need to turn the declaration into a RGBA, and once again, use the alpha channel to make it either fully transparent or fully opaque. Pretty much the same thing as before, only running RGBA instead of HSLA. The darker shade is obtained by subtracting 50 from each channel.

There's a really weird bug on WebKit on iOS that will show a black border instead of the appropriate color if using variables in the RGBA parameters of the shorthand border declaration. The solution is declaring the border in longhand.
Thanks Joel for pointing out the bug!

.btn { /* (...) */ /* applies a darker border if the lightness is higher than the border threshold */ --border-alpha: calc((var(--perceived-lightness) - var(--border-threshold)) * 100); border-width: .2em; border-style: solid; border-color: rgba(calc(var(--red) - 50), calc(var(--green) - 50), calc(var(--blue) - 50), var(--border-alpha)); }

Since we lost our initial HSL background declaration, our secondary theme color needs to be obtained via hue rotation:

.btn--secondary { filter: hue-rotate(60deg); }

This is not the best thing in the world. Besides applying the hue rotation to potential child elements as previously discussed, it means the switch to black/white and border visibility on the secondary element will depend on the main element’s hue and not on its own. But as far as I can see, the JavaScript implementation has the same issue, so I’ll call it close enough.

And there we have it, this time for good.

See the Pen CSS Automatic WCAG contrast font-color depending on element background by Facundo Corradini (@facundocorradini) on CodePen.

A pure CSS solution that achieves the same effect as the original JavaScript approach, but significantly cuts on the footprint.

Browser support

IE is excluded due to the use of CSS variables. Edge doesn’t have that capping behavior we used throughout. It sees the declaration, deems it nonsense, and discards it altogether as it would any broken/unknown rule. Every other major browser should work.

The post Switch font color for different backgrounds with CSS appeared first on CSS-Tricks.

Five interesting ways to use Sanity.io for image art direction

Thu, 08/09/2018 - 3:57am

When we saw Chris put up a list of cloud-hosted data-stores, we couldn't resist letting him know that we also had one of those, only ours is a fully featured CMS that come with a rich query language and an open source, real time, collaborative authoring tool that you can tailor to your specific needs using React. It's called Sanity.io.

“Add us to your list!” we asked Chris. “No, your stuff is interesting, can’t you write about you,” he replied. “Maybe something that would be useful for people working with images.” Challenge accepted!

Systems like Sanity wants to free your content from the specific page it happens to be sitting on, so that you can flow it through APIs. That way you can reuse your painstakingly crafted content anywhere you need it.

So, what does this mean for images?

Images are the odd ones out. We can capture documentation articles, pricing plans or project portfolios as potentially complex, but ultimately graspable data-structures that machines can query and process in many useful ways. But images? They're really just bags of pixels sitting at the end of a CDN, aren't they?

We came up with some ideas anyway because everyone needs images and images should be just as willing to travel to new and exciting places and services as the rest of your structured stuff.

So, here we are with a medium-sized bag of image tricks you can pull off with Sanity.

1. Grab the palette from image metadata

Ever tried to overlay text over a random image submitted by a user? How did that work out? We've usually ended submitting our images to filters till they're predictable and skulked home with a moody expression.

We thought it would be nice to ship an image palette with your pictures so you can select between dark and light typography as well as pair visual elements with image color. This makes legibility so much better and prevents clashes between the colors of images and text.

See the Pen Access image colors as metadata in Sanity by Knut Melvær (@kmelve) on CodePen.

2. Get low-quality image placeholders

However fast and nearby your CDN is, it still takes time to transmit large image files. So, we included a Low-Quality Image Placeholder (LQIP) with your image metadata. It's a base64 encoded string of your photo at 20px width. Getting LQIP in the first request lets you bake a proxy image right into the HTML.

<figure style={{ backgroundImage: `url(${mainImage.metadata.lqip})`, paddingTop: `calc(100% / ${mainImage.metadata.dimensions.aspectRatio})` }} > <img src={`${mainImage.url}`} /> </figure>

A challenge with image placeholders is being able to predict the dimensions of the image in a fluid layout. In this example, we used the aspect ratio that also comes in the metadata object to calculate the padding-top trick. That means that you don't have to calculate the aspect ratio yourself, and e.g. output it as a custom property combined with CSS variables.

See the Pen Get Low Quality Image Placeholders out of the box by Knut Melvær (@kmelve) on CodePen.

3. Use on-demand image transforms

You should be able to store archival originals and get the image in whatever resolution and format you need. Go ahead, upload your 268 megapixel TIFF, and ask for a 128x128 cropped JPEG with a 50% blur.

The transforms are generated from URL-parameters and generated on the first request. We cache the result locally and on a CDN so many requests for the same result image will be super snappy.

Take this image of the Carina Nebula. We uploaded a 29.566 x 12.960 version of it. Let’s say we wanted a PNG version with a 500px width. Attach ?w=500&fm=png to the image URL, and there it is:

Source File

Not only is it hard to hear screaming in space, it's also hard to judge directions. But let’s say you need to rotate the image. Well, you can append &or=90 and it's tilted 90 degrees (and scale it down to 128px):

Source File

Not to ruffle any feathers at NASA, but if you want to simulate spherical aberration on your new 1.5bn USD telescope, you can do that by adding ?blur=30 to the image-url!

Source File

The image pipeline is good for your SVGs as well. Say you need to offer a JPG Open Graph (og:image) to Facebook or you have an urgent need to add this SVG of a forklift as a Slack emoji:

Source File

Get the 128x128 PNG version you need, by appending the URL parameters w=128&h=128&fm=png just like this.

You also have control of background color, with bg=<hexcolor> Let's say we, for some reason, wanted this forklift on a lovely purple background.

And, finally, if you want to link to this image and trigger a download for the user, you can append dl=<suggested file-name> to make it happen.

Download the Image

It's not always as fun as this to mess around with URL parameters, so we made a JavaScript library that makes it a bit easier. Install it with npm install --save @sanity/image-url and learn how to use it (we use it in the demos throughout this post).

4. Crop and scale to fit around a focus point

Those pesky images with their fixed aspect ratios. In addition to not knowing the pixel density of the output device, you often don't even know what crop would be best suited for the layout. Even on a single website, you'll often use the same image in different layouts, and often need to serve them up to third parties, like Facebook, that expects specific aspect ratios.

Custom crop and hotspot in Sanity Studio

To ease the pain of dealing with this, we've let content editors set hotspots and crops for images. We have also made a JavaScript package that does all the hard work of making the correct image transform URLs for you. Simply pass an image asset and the size you want and we'll figure out the crop and scale.

See the Pen Custom crop and hotspots by Knut Melvær (@kmelve) on CodePen.

5. Make a real-time photomap

If this isn't turning it up to 11, it's at least a good 10.5.

We can extract EXIF and geo-location data from an image. EXIF-data contains information about the camera state when the photo was taken (e.g. exposure, aperture, lens, if the flash went off etc). Due to privacy concerns (we take GDPR-compliance super seriously) we have turned the feature off by default, but if you set [options: { metadata: ['exif', 'location' ] } in your schema options, they will be included.

We have made a tiny demo that lets you upload an image and, if it has location data (e.g. typically those you take with your phone), it will automatically pop up in the map — because the API is real-time with listeners!

See the Pen Shared Realtime Photo Map by Knut Melvær (@kmelve) on CodePen.

If you want to test out Sanity and these features yourself, you can get started with writing npm i -g @sanity/cli && sanity init. Even though the CLI guides you through the installation process, it's worth checking out the documentation (if not for the illustrations alone) and there are always people that want to help out with the nitty gritty in our Slack group.

The post Five interesting ways to use Sanity.io for image art direction appeared first on CSS-Tricks.

New on Wufoo: Form Manager Beta, File Manager Beta, Entry Manager Beta

Thu, 08/09/2018 - 3:54am

Wufoo really is firing on all cylinders lately! As you may know, I've been using Wufoo here on this site, and pretty much every other site I've ever made, to power the web forms for over a decade. That's a dang long time, which more than proves to me Wufoo is a form solution to trust. But also a product that improves!

There is a new Form Manager, Entry Manager, and File Manager.

Enable the beta stuff through the Account menu:

The new Form Manager has a modern and clean look. You can tell this has been a massive cleanup and give them better place to iterate from:

The Entry Manager is the biggest upgrade so far. You can view more entries at a time and navigate between entires much easier. I quite like how each entry takes you through kind of a ghosted version of the form itself, seeing exactly what someone entered:

The Files Manager is a brand new thing! Wufoo forms can collect files easily, so if that's a thing you use, it might be mighty handy for you to be able to browse and manage those from the manager.

Those are great improvements for you for getting around and doing stuff within Wufoo, but of course, the most important thing is that you can build powerful forms very easily on Wufoo and integrate them anywhere you need to.

The post New on Wufoo: Form Manager Beta, File Manager Beta, Entry Manager Beta appeared first on CSS-Tricks.

Here’s how I recreated theory11’s login form — how would you do it?

Wed, 08/08/2018 - 11:11am

I ran across a super cool design for a login form over on the website theory11.com. Actually, the whole site and the products they make are incredibly well designed, there's just something about the clean and classy form that really got me. Plus, it just so happened that the CodePen Challenge that coming week was based on forms, so I took a few minutes and tried slapping it together.

Fadeout vector pattern

One of the things I thought was super classy was the way that vector wallpaper-eque pattern was not only there but faded out sort of radially. I didn't try to match the pattern exactly—I just grabbed one from the Assets Panel in CodePen and dropped it onto the <html> element as a SVG data URL background-image with a low fill-opacity. Then a radial gradient sits on top and creates the fading effect—a radial gradient with the same base background color that fades away.

:root { --gray: rgb(14, 19, 25); --gray--t: rgba(14, 19, 25, 0); } html { background: var(--gray) url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="28" height="49" viewBox="0 0 28 49"%3E%3Cg fill-rule="evenodd"%3E%3Cg id="hexagons" fill="%239C92AC" fill-opacity="0.1" fill-rule="nonzero"%3E%3Cpath d="M13.99 9.25l13 7.5v15l-13 7.5L1 31.75v-15l12.99-7.5zM3 17.9v12.7l10.99 6.34 11-6.35V17.9l-11-6.34L3 17.9zM0 15l12.98-7.5V0h-2v6.35L0 12.69v2.3zm0 18.5L12.98 41v8h-2v-6.85L0 35.81v-2.3zM15 0v7.5L27.99 15H28v-2.31h-.01L17 6.35V0h-2zm0 49v-8l12.99-7.5H28v2.31h-.01L17 42.15V49h-2z"/%3E%3C/g%3E%3C/g%3E%3C/svg%3E'); } body { margin: 0; height: 100vh; background: radial-gradient( ellipse at bottom center, var(--gray--t), var(--gray) 90% ); } Double border

If you peek at the double border code on theory11's site, you'll see it's done with a single 2px solid border on a parent and another on the child element, with a bit of padding to space them out. Perfectly fine way to do it, of course. But it reminded me of the fact that you can literally do double as a border style. You have very little control over the spacing, but hey, it's close!

form { border: 7px double #AA8B59; } What about them corner boxes?

I had fun building the rest of it out, but I stopped short of dealing with those corner boxes. I thought about it though! My first thought was psuedo elements, as those are wonderful little tools for adding extra flair without any extra HTML. Except... you only get two of those and we need four here. Turns out that's how they do it—they get four because they use both the parent and the child (to get the border).

Peter Schmalfeldt took a crack at doing it that way:

See the Pen theory11 sign in by Peter Schmalfeldt (@manifestinteractive) on CodePen.

Dan Wilson took an entirely different approach by doing it with multiple background gradients:

See the Pen theory11 sign in (add corner boxes with background gradients) by Dan Wilson (@danwilson) on CodePen.

Another possibility would have been border-image. I find use cases for border-image rather rare, and the syntax pretty hard to grok, but this is kind of the perfect situation for it. The property uses "9 slice scaling," so imagine an image being cut up like a tic-tac-toe board. Then each of those areas can repeat or stretch (or variations of those). A graphic like this brown shape:

Nine-sliced:

And all those non-corner sections repeating directionally to make whatever middle space is needed.

SVG has similar potential. Mike Riethmuller has a great post about that. I'll steal his demo here:

See the Pen Flexy SVG by Chris Coyier (@chriscoyier) on CodePen.

See how different parts of that SVG stretch differently when the viewport is resized? I'm sure that could be used for our form design here as well.

I find it fun how many ways there are to do something like this. There's the ol' fashioned way, where those corners are just some <div>s you position and style up as needed. Or you could get extra super fancy and use Houdini / Paint API to do it!

The post Here’s how I recreated theory11’s login form — how would you do it? appeared first on CSS-Tricks.

©2003 - Present Akamai Design & Development.