My favorite way to level up as a front-end developer is to do the work. Literally just build websites. If you can do it for money, great, you should. If the websites you make can help yourself or anyone else you care about, then that’s also great. In lieu of that, you can also make things simply for the sake of making them, and you’ll still level up. It’s certainly better than just reading about things!
Here’s some resources that encourage you to level up by building things for the sake of leveling up, if you’re up to it.
It looks like this recently launched, and it’s what inspired this post. This idea of giving people front-end work to do is enough to build a business around! Some of them are free, and some of them are not.
Other businesses have centered themselves around this too. HackerRank is all about getting jobs and hiring, so they’ve got a very strong agenda, but part of the way they do that is putting you through these skill tests (solving challenges) which are meant to asses you, but you can certainly learn from them too.
Here’s the classic move: find something you like on Dribbble, rebuild it. The @keyframers often do it. Tim Evko’s Practice site used to pick a shot for you (along with random GitHub issues and random coding challenges), but the Dribbble part appears broken at the moment. The other stuff still works!
Matthias Martin created 100 days of CSS challenges. They are all there for you to see, including entries from other people — but the point is for you to give it a shot yourself, of course.
Ace Front End has challenges that focus specifically on vanilla HTML, CSS, and JavaScript.
I just happened to notice that the first challenge is a drop down navigation menu, and it doesn’t handle things like aria-expanded. I’m not entirely sure how big of a problem that is and I don’t mean to pick on Ace Front End — it’s just a reminder that there could be problems with any of these challenges. But that doesn’t mean you can’t learn something from them.
Carry’s advice is to start with a real-world object—the interface of a gaming console or a calculator, for example—and to try to recreate it using only CSS. “A great way to push the boundaries with a language is to make something that the language wasn’t meant to be doing in the first place,” he says.
This seems like a good time to plug our very own little :nth Tester tool. It definitely helps me understand what something like (2n+1) means in English. You can type in any string you like and see what effect that has on your site:
Anyway, back to Eric’s post. As he mentions, his technique is pseudo-random in that it looks like a random image on the page but it technically isn’t. Either way, I think it’s a really lovely technique! And it certainly breaks up the visual monotony that happens when you’re looking at a website for too long.
Here’s what it looks like in practice:
Lovely stuff!
Another way to do this is to use random numbers in CSS. For example, we could set a variable in JavaScript and then apply it with CSS custom properties. Or we could put all the images in a single sprite file and change the background-position based on a random number.
This is definitely one of those things in CSS where there are no wrong answers; just different ways to do the same awesome thing!
Here’s one simple, practical way to make apps perform better on mobile devices: always configure HTML input fields with the correct type, inputmode, and autocomplete attributes. While these three attributes are often discussed in isolation, they make the most sense in the context of mobile user experience when you think of them as a team.
There’s no question that forms on mobile devices can be time-consuming and tedious to fill in, but by properly configuring inputs, we can ensure that the data entry process is as seamless as possible for our users. Let’s take a look at some examples and best practices we can use to create better user experiences on mobile devices.
Using the correct input type
This is the easiest thing to get right. Input types, like email, tel, and url, are well-supported across browsers. While the benefit of using a type, like tel over the more generic text, might be hard to see on desktop browsers, it’s immediately apparent on mobile.
Choosing the appropriate type changes the keyboard that pops up on Android and iOS devices when a user focuses the field. For very little effort, just by using the right type, we will show custom keyboards for email, telephone numbers, URLs, and even search inputs.
One thing to note is that both input type="email" and input type="url" come with validation functionality, and modern browsers will show an error tooltip if their values do not match the expected formats when the user submits the form. If you’d rather turn this functionality off, you can simply add the novalidate attribute to the containing form.
A quick detour into date types
HTML inputs comprise far more than specialized text inputs — you also have radio buttons, checkboxes, and so on. For the purposes of this discussion, though, I’m mostly talking about the more text-based inputs.
There is a type of input that sits in the liminal space between the more free-form text inputs and input widgets like radio buttons: date. The date input type comes in a variety of flavors that are well-supported on mobile, including date, time, datetime-local, and month. These pop up custom widgets in iOS and Android when they are focused. Instead of triggering a specialized keyboard, they show a select-like interface in iOS, and various different types of widgets on Android (where the date and time selectors are particularly slick).
I was excited to start using native defaults on mobile, until I looked around and realized that most major apps and mobile websites use custom date pickers rather than native date input types. There could be a couple reasons for this. First, I find the native iOS date selector to be less intuitive than a calendar-type widget. Second, even the beautifully-designed Android implementation is fairly limited compared to custom components — there’s no easy way to input a date range rather than a single date, for instance.
Still, the date input types are worth checking out if the custom datepicker you’re using doesn’t perform well on mobile. If you’d like to try out the native input widgets on iOS and Android while making sure that desktop users see a custom widget instead of the default dropdown, this snippet of CSS will hide the calendar dropdown for desktop browsers that implement it:
One final thing to note is that date types cannot be overridden by the inputmode attribute, which we’ll discuss next.
Why should I care about inputmode?
The inputmode attribute allows you to override the mobile keyboard specified by the input’s type and directly declare the type of keyboard shown to the user. When I first learned about this attribute, I wasn’t impressed — why not just use the correct type in the first place? But while inputmode is often unnecessary, there are a few places where the attribute can be extremely helpful. The most notable use case that I’ve found for inputmode is building a better number input.
While some HTML5 input types, like url and email, are straightforward, input type="number" is a different matter. It has some accessibility concerns as well as a somewhat awkward UI. For example, desktop browsers, like Chrome, show tiny increment arrows that are easy to trigger accidentally by scrolling.
So here’s a pattern to memorize and use going forwards. For most numeric inputs, instead of using this:
<input type="number" />
…you actually want to use this:
<input type="text" inputmode="decimal" />
Why not inputmode="numeric" instead of inputmode="decimal" ?
The numeric and decimal attribute values produce identical keyboards on Android. On iOS, however, numeric displays a keyboard that shows both numbers and punctuation, while decimal shows a focused grid of numbers that almost looks exactly like the tel input type, only without extraneous telephone-number focused options. That’s why it’s my preference for most types of number inputs.
Christian Oliff has written an excellent article dedicated solely to the inputmode attribute.
Don’t forget autocomplete
Even more important than showing the correct mobile keyboard is showing helpful autocomplete suggestions. That can go a long way towards creating a faster and less frustrating user experience on mobile.
While browsers have heuristics for showing autocomplete fields, you cannot rely on them, and should still be sure to add the correct autocomplete attribute. For instance, in iOS Safari, I found that an input type="tel" would only show autocomplete options if I explicitly added a autocomplete="tel" attribute.
You may think that you are familiar with the basic autocomplete options, such as those that help the user fill in credit card numbers or address form fields, but I’d urge you to review them to make sure that you are aware of all of the options. The spec lists over 50 values! Did you know that autocomplete="one-time-code" can make a phone verification user flow super smooth?
Speaking of autocomplete…
I’d like to mention one final element that allows you to create your own custom autocomplete functionality: datalist. While it creates a serviceable — if somewhat basic — autocomplete experience on desktop Chrome and Safari, it shines on iOS by surfacing suggestions in a convenient row right above the keyboard, where the system autocomplete functionality usually lives. Further, it allows the user to toggle between text and select-style inputs.
On Android, on the other hand, datalist creates a more typical autocomplete dropdown, with the area above the keyboard reserved for the system’s own typeahead functionality. One possible advantage to this style is that the dropdown list is easily scrollable, creating immediate access to all possible options as soon as the field is focused. (In iOS, in order to view more than the top three matches, the user would have to trigger the select picker by pressing the down arrow icon.)
You can use this demo to play around with datalist:
CodePen Embed Fallback
And you can explore all the autocomplete options, as well as input type and inputmode values, using this tool I made to help you quickly preview various input configurations on mobile.
In summary
When I’m building a form, I’m often tempted to focus on perfecting the desktop experience while treating the mobile web as an afterthought. But while it does take a little extra work to ensure forms work well on mobile, it doesn’t have to be too difficult. Hopefully, this article has shown that with a few easy steps, you can make forms much more convenient for your users on mobile devices.
Digital space obviously has its challenges, but it also provides incredible opportunities for us to connect and learn in ways we just wouldn’t be able to do otherwise. The situation with COVID-19 has challenged us to consider ways in which we could offer a similar SmashingConf experience and access to experts just as in an in-person workshop — without needing to leave your desk.
With insightful takeaways, exercises, access to slides, recordings and friendly Q&As, it has been such an incredible experience already! We’ve had literally people from all over the world collaborating together on group exercises — something we’d never be able to achieve with an in-person event.
And we’re just getting started! We already have a schedule of online workshops ready for you so you can start marking your calendars and join us anytime you like. What better way is there to boost your skills online and learn practical, actionable insights from experts in the industry — live!
Do you like what you see, but are worried about getting some time off from work? Well, you surely didn’t think we would leave your hanging? We know how difficult it can sometimes be, and so we’ve prepared a neat lil’ Convince-Your-Boss template to help you out. Good luck!
That’s right! Paul Boag’s Click! Encourage Clicks Without Shady Tricks is currently in its final production stage and the pre-release starts on May 5. This practical guide has 11 chapters full of advice that can help you start improving your conversion rate in just a matter of simple steps. You can subscribe for a pre-order discount and be one of the first to get your hands on the book. Stay tuned!
Live UX Review With The Author
Next week, we’ll be hosting a Smashing TV webinar with Paul Boag who’ll be reviewing your websites and sharing some techniques you can use to improve conversion rates — without having to resort to any shady tricks. Tell me more ?
Also, in case you missed it, there is a Smashing Podcast episode featuring two of the authors of the book: Trine Falbe and Martin Michael Frederiksen. They discuss what it means for a design to be ethical, and how we can make improvements in our own projects.
We publish a new article every day on various topics that are current in the web industry. Here are some that our readers seemed to enjoy the most and have recommended further:
“Best Practices With React Hooks” by Adeneye David Abiodun This article covers the rules of React Hooks and how to effectively start using them in your projects. Please note that in order to follow this article in detail, you will need to know how to use React Hooks.
“Inspired Design Decisions With Herb Lubalin” by Andy Clarke How can we combine elements to develop powerful headers and calls to action? How do we use pre-formatted HTML text, and the text element in SVG for precise control over type? How can we optimise SVGs and make SVG text accessible? In this article, we’ll explore just that.
“Baking Structured Data Into The Design Process” by Frederick O’Brien Retrofitting search engine optimization only gets you so far. As metadata gets smarter, it’s more important than ever to build it into the design process from the start.
“How To Make Life Easier When Using Git” by Shane Hudson You don’t need to know your trees from your dangling blobs. If you use Git every day and feel like it’s a juggling act, then here are some tricks and tips to help make your life a bit easier.
Best Picks From Our Newsletter
We’ll be honest: Every second week, we struggle with keeping the Smashing Newsletter issues at a moderate length — there are just so many talented folks out there working on brilliant projects! Kudos to everyone involved!
Interested in sponsoring? Feel free to check out our partnership options and get in touch with the team anytime — they’ll be sure to get back to you right away.
Tips For Leading A Remote Team
Leading a remote design team can feel a bit daunting, especially if it’s your first time. Luckily, other people out there have found themselves in the same situation before and developed strategies to keep the team productive and effective, no matter where everyone might be located. Mark Boulton is one of them.
In light of recent events when many teams need to switch to remote work, Mark summarized some simple but useful approaches that have helped him leading remote teams for years. From continuing your team’s rituals to dealing with expectations on availability and coaching people through the ups and downs that working remotely brings along, Mark’s tips aren’t hard to adopt but they can make a real difference. (cm)
Getting To Grips With CSS Viewport Units
CSS Viewport units provide us with a way to size things in a fluid and dynamic way, without the need for JavaScript. If you haven’t gotten around to dive deeper into the topic yet, Ahmad Shadeed wrote a useful guide to CSS Viewport units.
Starting with a general overview of the viewport units vw, vh, vmin, and vmax, the guide covers how viewport units differ from percentages and explores practical use cases for viewport units and how to implement them in your projects. Just the push you might have needed to make the switch. (cm)
A Better File Uploader For The Web
Building a better file uploader for the web. That was the idea behind the JavaScript image uploader Uppload. Created by Anand Chowdhary, the image uploader is open-source and can be used with any file uploading backend. And with more than 30 plugins, it’s highly customizable, too.
Users can drag and drop their files to upload them or import from a camera, URL, or social media and a several other services (there’s even an option to take and upload a screenshot just by entering a URL). During the uploading process, users can apply effects to the images and adjust filters like brightness, contrast, and saturation. If that’s overkill for your project, you can select only what you need and treeshake the rest, of course. Uppload supports browsers down to IE10. Handy! (cm)
Open-Source Flip Counter Plugin
Do you want to count down to an event, visualize a fundraising campaign, or show a clock or sales counter? Then Rik Schennink’s Flip Counter might be for you. The plugin is open-source, mobile-friendly, easy to set up, and it gets by without any dependencies.
Apart from its ease of use and flexibility, Flip shines with the beautifully smooth animation that is used to flip the numbers on the cards. Depending on your use case, there are several presets that you can use as a starting point to build your flip counter. The visual style can be customized with CSS. A lovely little detail. (cm)
How To Write Good Email Code
Maybe you’ve been in that situation before where you had to code an HTML email but struggled with email code best practices. To help you master the challenge, Mark Robbins set up a library for good email code. You can simply copy and paste the code and use it in your emails or you can learn more about the theory behind it.
Priority lies in making sure the code is semantic, functional, accessible, and meeting user expectations, as Mark points out. Consistency between email clients and pixel perfect design are important, too, but always secondary. One for the bookmarks. (cm)
A Complete Solution For Tooltips, Popovers, And Dropdowns
If you’re looking for a quick and easy solution for tooltips, popovers, dropdowns, and menus, you might want to take a look at Tippy.js. The library provides the logic and styling involved in all types of elements that pop out from the flow of your document and get overlaid on top of the UI.
Tippy.js is optimized to prevent flipping and overflow, it’s WAI-Aria compliant, works in all modern browsers, and, so the promise, it even delivers high performance on low-end devices. You can style the elements with custom CSS and TypeScript is supported out of the box, too. Handy! (cm)
Open-Source Tool To Make Animated Product Mockups
What do you do when you’re missing a tool for a specific purpose? You build it yourself. That’s what Alyssa X did when she was looking for a tool to make animated GIFs and videos to showcase a product. Her take on the subject: Animockup.
With Animockup, you can showcase your product in action within a device mockup. Just drag some screen footage into the browser-based tool, and Animockup automatically places it into your desired mockup. You can add text, images, and adjust the styling, and choose from a selection of presets to optimize your mockup for sharing on Twitter, Dribble, Instagram and the like. A useful little helper. (cm)
Create CSS Color Gradients With Ease
Hand-picking colors to make a color gradient requires design experience and a good understanding of color harmony. If you need a gradient for a background or for UI elements but don’t feel confident enough to tackle the task yourself (or if you’re in a hurry), the color gradient generator which the folks at My Brand New Logo have created has got your back.
Powered by color gradient algorithms, the generator creates well-balanced gradients based on a color you select. There are four different styles of gradients that go from subtle to a mother-of-pearl effect and an intense, deep color gradient. You can adjust the gradient with sliders and, once you’re happy with the result, copy-paste the generated CSS code to use it in your project. Nice! (cm)
Collaborative Diagrams
Pen and paper are often hard to beat when you want to visualize an idea with a quick diagram. If you’re looking for a digital alternative that is just as straightforward and easy to use as your analog tools, you might want to check out Excalidraw.
Excalidraw is a virtual whiteboard that you can draw on. You can choose from a set of shapes, connect them with arrows or lines, add text, and color. There are some other styling options, too, but the tool is kept rather simple so that you can focus on what’s really important: visualizing your idea. A great feature that comes in especially handy now that a lot of teams work remotely: You can share a live-collaboration session with your team members or your clients. Export and save options are included, too, of course. (cm)
Mastering BEM Naming Conventions
BEM makes your code scalable and reusable, prevents it from becoming messy, and facilitates teamwork. However, even experienced CSS developers struggle with the naming conventions sometimes. To prevent you from getting lost in the BEM cosmos, the folks at 9elements put together the BEM Cheat Sheet with naming suggestions for some of the most common web components: breadcrumb navigation, buttons, cards, lists, tabs, form checkboxes, sidebars, and more.
If you want to dive in even deeper into the BEM methodology, Luke Whitehouse shares tips to tackle an ever-present issue in BEM: grandchildren, i.e. elements that are tied to another element, rather than to the block itself. Luke explores three different approaches to master the challenge: flattening the grandchildren and treating them as if they have no relation with their parent element, by creating new blocks, and by extending the BEM naming convention. A good read. (cm)
A Preserve For Classic Games
Do you feel nostalgic when you think of the video games you played back in the 80s and 90s? Well, why not take a little trip back to those days when games were just as much fun without the fancy effects they shine with today?
ClassicReload preserves more than 6,000 old retro games and abandoned OD/interfaces that you can play right in your browser. You can search for your favorite or browse the games by name, year, genre, and platform to discover something new. No matter if it’s The Oregon Trail, Prince of Persia, or Dangerous Dave you’ve been longing for for so long, if you’ve got a sweet spot for games, the site will keep you entertained for quite a while. (cm)
Managing HTML DOM And jQuery Alternatives
How do you manage HTML DOM with vanilla JavaScript only? Phuoc Nguyen collected 100 native DOM scripting snippets along with explanations on how to use them. The snippets are labeled by difficulty and range from basic (e.g. detecting if an element is focused) to more intermediate tasks like exporting a table to CSV and, finally, advanced use cases like creating a range slider.
Speaking of going vanilla: If you’re using jQuery in your projects, it might be a good idea to check if you actually need the additional dependency or if a few lines of utility code could do the trick. “You might not need jQuery” lists useful alternative code snippets that help you forgo jQuery. (cm)
Overly Descriptive Color Palettes
Have you ever considered combining snail-paced soft pink with unsealed mahogany and lousy watermelon as a color scheme for your next project? Well, what might sound a bit weird at first, is the concept behind colors.lol, a color inspiration site with “overly descriptive color palettes”, as its creator Adam Fuhrer describes it.
Created as a fun way to discover interesting color combinations, the palettes are hand-selected from the Twitter bot @colorschemez. The feed randomly generates color combinations and matches each color with an adjective from a list of over 20,000 words. Hiding behind the unusual names are of course real hex color values that you can use right away — #FDB0C0, #4A0100, and #FD4659 in the case of snail-paced soft pink and its fellas, for example. A fun take on color. (cm)
Flexible Repeating SVG Masks
Sometimes it’s a small idea, a little detail in a project that you tinker with and that you can’t let go off until you come up with a tailor-made solution to make it happen. Nothing that seems like a big deal at first glance, but that requires you to think outside the box. In Tyler Gaw’s case, this little detail was a flexible header with a little squiggle at the bottom instead of a straight line. The twist: to make the component future-proof, Tyler wanted to use a seamless, horizontal repeating pattern that he could color with CSS in any color he liked.
To get the job done, Tyler settled on flexible repeating SVG masks. SVG provides the shape, CSS handles the color, and mask-image does the heavy lifting by hiding anything in the underlying div that doesn’t intersect with the shape. A clever approach that can be used as the base for some fun experiments. (cm)
As a token of appreciation, Vitaly Friedman released his very own “Smart Interface Design Checklists”, a PDF deck with 150+ questions to ask when designing and building anything from hamburgers to carousels and tables. Subscribe to the newsletter below and get it in your inbox right away!
Smashing Newsletter
Every second Tuesday, we send a newsletter with useful techniques on front-end and UX. Subscribe and get Smart Interface Design Checklists PDF in your inbox.
Front-end, design and UX. Sent 2× a month. You can always unsubscribe with just one click.
Today, we’re going to look at what web standards are, why we have them, and what you actually need to do with them as a web designer.
What Are Web Standards?
When we talk about web standards, what we’re referring to are formal specifications that the Internet and everything on it should adhere to. So, this is frequently less about how the frontend of a website appears and more about how the backend of it is structured.
Web standards aren’t just focused on web development either. They touch on browsers, HTTP, design and development software, as well as consumer devices. Essentially, web standards are developed and formalized to bring strength and consistency to the very core of the web. The more we adhere to these standards, the more accessible the web becomes for all.
Even if you’re not involved in the coding of your websites, you’re likely familiar with today’s web standards:
Valid HTML, CSS, and JavaScript
Poorly written code can cause a lot of problems for the performance of a website, not to mention the bugs it can introduce. So, this was one of the first things we needed to get a handle on.
Since HTML, CSS, and JavaScript form the backbone of the web, there are strict standards pertaining to how they’re written and when they’re used. In addition, as variations of these languages enter the web’s lexicon — like HTML5 and CSS3 — standards are created for them as well.
By standardizing coding, we make it possible for all developers and designers to speak the same language, and for every web browser or software to comprehend them.
Graphics
This is an important one for web designers, though it’s not so much as a strict standard as a set of best practices for using graphics on the web. For example, this is what the W3C recommends:
PNG for photos;
SVG for data visualization;
CSS for enhancing basic HTML;
Canvas API for creating gradients, shapes, and other design effects;
WebCGM for vector graphics.
If you want your website to perform as efficiently as possible, it’s important to take recommendations like these seriously.
Mobile Responsiveness
With the proliferation of smart devices and the immense variation in the types of devices available, it’s become critical to have standards for the mobile web.
That said, standards bodies haven’t just standardized responsive design. They’ve also created a set of best practices for the mobile web.
Guidelines aren’t just provided for design or tools used either. They also focus on things like processing payments, website security, and performance.
Unlike some of the other web standards mentioned here, standardizing accessibility is a big deal. As you can see in the screenshot above, it’s not just something that affects how web developers code or web designers create. It affects everyone who contributes to a website — writers, testers, project managers, policymakers, and so on.
There’s a lot to unpack when it comes to accessibility standards. Suffice to say, if you’re building a website that aims to actively serve the public, then every one of these standards needs to become part of your workflow.
Where Do Web Standards Come From?
In the very early days of the web, the browser wars between Internet Explorer and Firefox was problematic. As they attempted to compete for greater market share, their technologies diverged wildly. If left to their own devices, those browsers could’ve done real damage to the Internet, causing a fractured experience from browser to browser, and website to website.
Tim Berners-Lee, the person who founded the world wide web, decided something needed to be done and, as a result, formed the World Wide Web Consortium (W3C).
Since the founding of the W3C, it’s been the mission of this standards organization to establish universal standards that would allow the web to grow in a positive direction.
The W3C wasn’t the only standards organization trying to improve the Internet in the early days. The Web Standards Project arose in the ‘90s to provide support to the W3C. Its specific mission was to help make the web less costly and complex to build for and manage. Although it disbanded in 2013, it played a critical role in getting web browsers to support HTML 4 and XHTML.
Today, there are other standards organizations helping to bring order and control to the web. These are some of the bigger ones operating today:
Ecma has been around since the ‘60s. Its aim has been to standardize communication and information systems. It’s also responsible for developing ECMAScript, which standardized JavaScript.
Internet Engineering Task Force (IETF) is dedicated to strengthening the architecture of the Internet while creating a more open environment.
The WHATWG Community has developed a number of standards and non-standards around things like URLs, encoding, APIs, and coding.
These organizations — like the early creators of the web — aren’t in it to make money. Their sole aim is to create a free, open, and efficient Internet for every user.
What Do We Need Web Standards For?
Last but not least, let’s talk about the why.
As far as users are concerned, one of the biggest benefits to them is the predictability of the web.
That’s not to say that web standards will prohibit you from being creative in how you design a website. However, in terms of how it functions and how your visitors can interact with it, those elements should be consistent with the rest of the web. This creates a more inviting environment for users as it removes the struggle and confusion that comes with entering new territory.
As far as web designers are concerned, I think that’s been made clear by now. Not only does it help you work more efficiently, but it allows you to contribute to a better web — one that’s well-built and accessible for all.
It’s incredibly sad that Christopher Schmitt passed away last week¹. I keep thinking about how Christopher was one of the best dudes I knew. Just incredibly kind and thoughtful all the way through. I know everyone says that about people after they pass, but I really mean it here. I’m sure we all know people that we like quite a bit, but hey, we know they can be an asshole sometimes too. Not Christopher, at least not to me. He was always good to me in ways I’ll never be able to reciprocate.
Here’s the most important thing:
We (a group of developer friends) would like to build a site of memories that people have of Christopher. We have our own, but we’d like to collect as many memories from as many people as possible to make this a true memorial to him. If you have one you would like to share publicly, please submit it to this little site just for that.
I hardly know where to start reminiscing about Christopher. Broadly: I spoke at more conferences thrown by Christopher (and Ari) than anyone else. At least a dozen. He used to throw on called In Control in Orlando that I spoke at a number of times and met some early web friends that I’m still close with today! Stephanie Rewis mentioned to me that’s where we met the first time and she remembers me coding away on a yet-to-be released CodePen while there. In Control went to Hawaii one year, along with the first-ever CSS Dev Conf, brining me to Hawaii for the first time in my life, where I remember driving around the island with Daniel Burka. CSS Dev Conf went all sorts of amazing places, and I went to all of them! Estes Park, New Orleans, The Queen Mary… all these incredible locations that were all incredible memories for me.
And all I have are blurry, distant photos of him like this:
Christopher and Ari also ran an absolute ton of online conferences. CSS Summit, RWD Summit, SVG Summit… they ran a ton of them and they were really ahead of their time. I remember a funny moment where I had just moved to a new city and my internet wasn’t installed yet, but I still had to present online at one of these, so I did it out of my neighbors garage (as their kids slept), and the light in the garage kept going out because it didn’t detect movement. ?
The people I met through Christopher are too many to count. He was a great connector in that way. His influence on the web was tremendous through his direct work, like writing (what, a dozen books?), blogging (even here!), speaking, and podcasting (e.g. The Non Breaking Space Show) but when you consider the people he brought together, his influence is immeasurable.
I literally made money off the guy. In the early years of ShopTalk, when it was tough to sell podcast spots at all, Christopher would sponsor every episode of ShopTalk, which probably gave us the boost that ensured it’s still happening today. Who knows what would have happened without that.
One of the things I liked best about Christopher is that he was a damn nerd. Through and through. In a way that made you envious that you could one day hope to up your nerd level that high. Guy had a Chewbacca costume that he wore regularly. His Instagram is full of snaps of him meeting other famous nerds.
I hope you still got those black Converses on wherever you are, buddy.
(And remember, if you have a memory with Christopher, please share it.)
It wasn’t COVID-19. Poor guy was dealt bad cards from day one for his health and he fought it the the best he could.
Before the advent of CSS custom properties (we might call them “variables” in this article as that’s the spirit of them), implementing multiple color schemes on the same website usually meant writing separate stylesheets. Definitely not the most maintainable thing in the world. Nowadays, though, we can define variables in a single stylesheet and let CSS do the magic.
Even if you aren’t offering something like user-generated or user-chosen color themes, you might still use the concept of theming on your website. For example, it is fairly common to use different colors themes across different areas of the site.
We’re going to build out an example like this:
In this example, all that changes between sections is the color hue; the variations in lightness are always the same. Here’s an example of a simplified color palette for a specific hue:
CodePen Embed Fallback
A palette of multiple hues might look something like this:
CodePen Embed Fallback
This would take effort to do with RGB color value, but in HSL only one value changes.
The syntax is very similar to traditional CSS. Here is an overview of the basic usage:
CodePen Embed Fallback
It’s common to see variables defined on the :root pseudo-element, which is always in HTML, but with higher specificity. That said, variables can be defined on any element which is useful for scoping specific variables to specific elements. For example, here are variables defined on data attributes:
CodePen Embed Fallback
Adding calc() to the mix
Variables don’t have to be fixed values. We can leverage the power of the calc() function to automatically calculate values for us while adhering to a uniform pattern:
What we’re basically trying to do is change the color of the same component on different sections of the same page. Like this:
CodePen Embed Fallback
We have three sections in tabs with their own IDs: #food, #lifestyle, and #travel. Each section corresponds to a different hue. The data-theme-attribute on the div.wrapper element defines which hue is currently in use.
When #travel is the active tab, we’re using the --first-hue variable, which has a value of 180°. That is what gets used as the --hue value on the section, resulting in a teal color:
Clicking any of the tabs updates the data-theme attribute to the ID of the section, while removing the hash (#) from it. This takes a smidge of JavaScript. That’s one of the (many) nice things about CSS: they can be accessed and manipulated with JavaScript. This is a far cry from preprocessor variables, which compile into values at the build stage and are no longer accessible in the DOM.
<li><a href="#food">Food</a></li>
const wrapper = document.querySelector('.wrapper');
document.querySelector("nav").addEventListener('click', e => {
e.preventDefault();
e.stopPropagation();
// Get theme name from URL and ditch the hash
wrapper.dataset.theme = e.target.getAttribute('href').substr(1);
})
Progressive enhancement
When we use JavaScript, we should be mindful of scenarios where a user may have disabled it. Otherwise, our scripts — and our UI by extension — are inaccessible. This snippet ensures that the site content is still accessible, even in those situations:
document.querySelectorAll('section').forEach((section, i) => {
if (i) { // hide all but the first section
section.style.display = 'none';
}
})
This merely allows the tabs to scroll up the page to the corresponding section. Sure, theming is gone, but providing content is much more important.
While I chose to go with a single-page approach, it’s also possible to serve the sections as separate pages and set [data-theme] on the server side.
Another approach
So far, we’ve assumed that color values change linearly and are thus subject to a mathematical approach. But even in situations where this is only partially true, we may still be able to benefit from the same concept. For instance, if lightness follows a pattern but hue doesn’t, we could split up the stylesheet like this:
Web components are an exciting (and evolving) concept. It’s enticing to think we can have encapsulated components that can be reused anywhere and theme them on a case-by-case basis. One component with many contexts!
We can use CSS variable theming with web components. It requires us to use a host-context() pseudo-selector. (Thanks to habemuscode for pointing this out to me!)
Theming a website with CSS custom properties is much easier than the workaround approaches we’ve resorted to in the past. It’s more maintainable (one stylesheet), performant (less code), and opens up new possibilities (using JavaScript). Not to mention, CSS custom properties become even more powerful when they’re used with HSL colors and the calc() function.
We just looked at one example where we can change the color theme of a component based on the section where it is used. But again, there is much more opportunity here when we start to get into things like letting users change themes themselves – a topic that Chris explores in this article.
Jetpack has had a search feature for a while. Flip it on, and it replaces your built-in WordPress search (which is functional, but not particularly good) with an Elasticsearch-powered solution that is faster and has better results. I’ve been using that for quite a while here on CSS-Tricks, which was an upgrade from using a Google Custom Search Engine.
Jetpack just upped their game again with a brand new search upgrade. You can use Jetpack search how you were already, or you can flip on Instant Search and take advantage of this all-new search experience (inside and out).
A Full Page Experience
Instant Search provides a full page-covering search experience. I think it’s awesome. When a user is searching, that’s the mindset they are in and giving them all the space they need to accomplish that goal is great. Here’s me searching (video):
Best I can tell, CSS-Tricks gets a couple hundred thousand on-site searches per month, so having a great experience there is very important to me. I don’t even wanna mess around with bad on-site search experiences, or products that are too expensive. I’d rather send people to a site-scoped Google search than a bad on-site search. Fortunately, Instant Search is about as good of an on-site search experience as I can imagine, especially for the zero-work it takes to implement.
Design Control
You have some control over the look of things from the Customizer.
Instant Search is designed to work on any site, so you probably don’t need to do much. I was really surprised how well it worked out-of-the-box for CSS-Tricks. As a CSS control freak, I did ship a handful of design tweaks to it, but that’s just because I love doing that kind of thing.
Tweaks No Longer Needed
With the previous version of Jetpack Search, I had custom code in place to tweak Elasticsearch. I did things like factoring in comment counts as an indicator of popularity, so that I could be sure our best content was high in the results. Remember as powerful as this search is, it doesn’t have a model of the entire internet to calculate relevancy from like Google does. Good news though:
To further improve our search algorithm, we started experimenting with adding the percentage of pageviews from the past 30 days into the index. We ended up finding that pageviews are a much better ranking signal because it somewhat combines both popularity and recency. So now most of our result ranking is strongly influenced by the number of pageviews a post or page gets. Conveniently, if you get a lot of Google Search traffic, our search results should be heavily influenced by Google’s ranking algorithm.
Emphasis mine. With Jetpack Instant Search, I was able to rip all that custom code out (removing code always feels great) because the new algorithms are doing a great job with ranking results.
Pricing
Now Jetpack Search is ala-carte rather than baked into specific plans. Don’t need it, you don’t pay for it? Only need this feature? You can buy it regardless of what plan you are on.
I’m told the pricing is about scope. Jetpack plans are about features, not scale of site, but that doesn’t make much sense for search where the scale of the site matters a ton. So it’s a sliding scale based on the “records” you have, which are basically posts and pages.
I would think a lot of sites fall into the $25/month (15% off for annual) category. You probably mostly start caring about on-site search above 1,000 records and 10,000 records is a ton. I pay for the tier one up from that (~$612 a year) only because our (now archived) bbPress forums pushes the number over 10,000. That’s a perfectly fair price for this for a site like mine.
Wish List
My #1 thing is that I wish it was easy to remove certain things from search results. We have tons and tons of records from our bbPress forums that I made the (hard) call to close this year. Removing those records would pull me down into a smaller pricing tier, but more importantly, I’d rather just not show those results in search at all.
It’s not just CSS-Tricks being in an unusual situation. I’ve also turned on Jetpack Instant Search on the CodePen Documentation.
In that circumstance, I’d consider turning removing blog posts (believe it or not) from the search results, so instead just the Pages would show up which are our core documentation there. Or perhaps even better, blog posts are just turned off as a filter by default, but users could flip them on to see them in the results.
All in all, this is a huge upgrade to Jetpack and yet another reason I consider it the most important plugin I run on my WordPress sites. If you’re curious about other Jetpack features we use, we made a special page for that.
Before we get started, I want to note that Django’s built-in administrative capabilities, even after customization, are not meant for end-users. The admin panel exists as a developer, operator, and administrator tool for creating and maintaining software. It is not intended to be used to give end-users moderation capabilities or any other administrator abilities over the platform you develop.
This article is based on a hypothesis in two parts:
The Django admin panel is so intuitive that you basically already know how to use it.
The Django admin panel is so powerful that we can use it as a tool for learning about representing data in a relational database using a Django model.
I offer these ideas with the caveat that we will still need to write some configuration code to activate the admin panel’s more powerful abilities, and we will still need to use Django’s models-based ORM (object-relational mapping) to specify the representation of data in our system.
We’re going to be working with a sample project in this article. The project models some data that a library would store about its books and patrons. The example should be fairly applicable to many types of systems that manage users and/or inventory. Here’s a sneak peek of what the data looks like:
Please complete the following steps to get the example code running on your local machine.
1. Installing Packages
With Python 3.6 or higher installed, create a directory and virtual environment. Then, install the following packages:
pip install django django-grappelli
Django is the web framework that we’re working with in this article. (django-grappelli is an admin panel theme that we’ll briefly cover.)
2. Getting The Project
With the previous packages installed, download the example code from GitHub. Run:
git clone https://github.com/philipkiely/library_records.git
cd library_records/library
3. Creating a Superuser
Using the following commands, set up your database and create a superuser. The command-line interface will walk you through the process of creating a superuser. Your superuser account will be how you access the admin panel in a moment, so be sure to remember the password you set. Use:
For our exploration, I created a dataset called a fixture that you can load into the database (more on how to create a fixture at the end of the article). Use the fixture to populate your database before exploring it in the admin panel. Run:
python manage.py loaddata ../fixture.json
5. Running The Example Project
Finally, you’re ready to run the example code. To run the server, use the following command:
python manage.py runserver
Open your browser to http://127.0.0.1:8000 to view the project. Note that you are automatically redirected to the admin panel at /admin/. I accomplished that with the following configuration in library/urls.py:
from django.contrib import admin
from django.urls import path
from records import views
urlpatterns = [
path('admin/', admin.site.urls),
path('', views.index),
]
combined with the following simple redirect in records/views.py:
from django.http import HttpResponseRedirect
def index(request):
return HttpResponseRedirect('/admin/')
Using The Admin Panel
We’ve made it! When you load your page, you should see something like the following:
This view is accomplished with the following boilerplate code in records/admin.py:
from django.contrib import admin
from .models import Book, Patron, Copy
admin.site.register(Book)
admin.site.register(Copy)
admin.site.register(Patron)
This view should give you an initial understanding of the data that the system stores. I’ll remove some of the mystery: Groups and Users are defined by Django and store information and permissions for accounts on the system. You can read more about the User model in an earlier article in this series. Books, Copys, and Patrons are tables in the database that we created when running migrations and populated by loading the fixture. Note that Django naively pluralizes model names by appending an “s,” even in cases like “copys” where it is incorrect spelling.
In our project, a Book is a record with a title, author, publication date, and ISBN (International Standard Book Number). The library maintains a Copy of each Book, or possibly multiple. Each Copy can be checked out by a Patron, or could currently be checked in. A Patron is an extension of the User that records their address and date of birth.
Create, Read, Update, Destroy
One standard capability of the admin panel is adding instances of each model. Click on “books” to get to the model’s page, and click the “Add Book” button in the upper-right corner. Doing so will pull up a form, which you can fill out and save to create a book.
Creating a Patron reveals another built-in capability of the admin’s create form: you can create the connected model directly from the same form. The screenshot below shows the pop-up that is triggered by the green plus sign to the right of the User drop-down. Thus, you can create both models on the same admin page.
You can create a COPY via the same mechanism.
For each record, you can click the row to edit it using the same form. You can also delete records using an admin action.
Admin Actions
While the built-in capabilities of the admin panel are widely useful, you can create your own tools using admin actions. We’ll create two: one for creating copies of books and one for checking in books that have been returned to the library.
To create a Copy of a Book, go to the URL /admin/records/book/ and use the “Action” dropdown menu to select “Add a copy of book(s)” and then use the checkboxes on the left-hand column of the table to select which book or books to add a copy of to the inventory.
Creating this relies on a model method we’ll cover later. We can call it as an admin action by creating a ModelAdmin class for the Profile model as follows in records/admin.py:
from django.contrib import admin
from .models import Book, Patron, Copy
class BookAdmin(admin.ModelAdmin):
list_display = ("title", "author", "published")
actions = ["make_copys"]
def make_copys(self, request, queryset):
for q in queryset:
q.make_copy()
self.message_user(request, "copy(s) created")
make_copys.short_description = "Add a copy of book(s)"
admin.site.register(Book, BookAdmin)
The list_display property denotes which fields are used to represent the model in the model’s overview page. The actions property lists admin actions. Our admin action is defined as a function within BookAdmin and takes three arguments: the admin object itself, the request (the actual HTTP request sent by the client), and the queryset (the list of objects whose boxes were checked). We perform the same action on each item in the queryset, then notify the user that the actions have been completed. Every admin action requires a short description so that it can be properly identified in the drop-down menu. Finally, we now add BookAdmin when registering the model.
Writing admin actions for setting properties in bulk is pretty repetitive. Here’s the code for checking in a Copy, note its near equivalence to the previous action.
from django.contrib import admin
from .models import Book, Patron, Copy
class CopyAdmin(admin.ModelAdmin):
actions = ["check_in_copys"]
def check_in_copys(self, request, queryset):
for q in queryset:
q.check_in()
self.message_user(request, "copy(s) checked in")
check_in_copys.short_description = "Check in copy(s)"
admin.site.register(Copy, CopyAdmin)
Admin Theme
By default, Django provides fairly simple styles for the admin panel. You can create your own theme or use a third-party theme to give the admin panel a new look. One popular open-source theme is grappelli, which we installed earlier in the article. You can check out the documentation for its full capabilities.
Installing the theme is pretty straightforward, it only requires two lines. First, add grappelli to INSTALLED_APPS as follows in library/settings.py:
from django.contrib import admin
from django.urls import path, include
from records import views
urlpatterns = [
path('grappelli/', include('grappelli.urls')),
path('admin/', admin.site.urls),
path('', views.index),
]
With those changes in place, the admin panel should look like the following:
There are a number of other themes out there, and again you can develop your own. I’ll be sticking with the default look for the rest of this article.
Understanding Models
Now that you’re comfortable with the admin panel and using it to navigate the data, let’s take a look at the models that define our database structure. Each model represents one table in a relational database.
A relational database stores data in one or more tables. Each of these tables has a specified column structure, including a primary key (a unique identifier for each element) and one or more columns of values, which are of various types like strings, integers, and dates. Each object stored in the database is represented as a single row. The “relational” part of the name comes from what is arguably the technology’s most important feature: creating relationships between tables. An object (row) can have a one-to-one, one-to-many (foreign key), or many-to-many mapping to rows in other tables. We’ll discuss this further in the examples.
Django, by default, uses SQLite3 for development. SQLite3 is a simple relational database engine and your database is automatically created as db.sqlite3 the first time you run python manage.py migrate. We’ll continue with SQLite3 for this article, but it is not suitable for production use, primarily because overwrites are possible with concurrent users. In production, or when writing a system that you one day intend to deploy, use PostgreSQL or MySQL.
Django uses models to interface with the database. Using part of Django’s ORM, the records/models.py file includes multiple models, which allows for specifying fields, properties, and methods for each object. When creating models, we strive for a “Fat Model” architecture, within reason. That means that as much of the data validation, parsing, processing, business logic, exception handling, edge case resolution, and similar tasks as possible should be handled in the specification of the model itself. Under the hood, Django models are very complex, featureful objects with widely useful default behavior. This makes the “Fat Model” architecture easy to achieve even without writing a substantial amount of code.
Let’s walk through the three models in our sample application. We can’t cover everything, as this is supposed to be an introductory article, not the Django framework’s complete documentation, but I’ll highlight the most important choices I made in constructing these simple models.
The Book class is the most straightforward of the models. Here it is from records/models.py:
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=300)
author = models.CharField(max_length=150)
published = models.DateField()
isbn = models.IntegerField(unique=True)
def __str__(self):
return self.title + " by " + self.author
def make_copy(self):
Copy.objects.create(book=self)
All CharField fields require a specified max_length attribute. The conventional length is 150 characters, which I doubled for title in case of very long titles. Of course, there still is an arbitrary limit, which could be exceeded. For unbounded text length, use a TextField. The published field is a DateField. The time the book was published doesn’t matter, but if it did I would use a DateTimeField. Finally, the ISBN is an integer (ISBNs are 10 or 13 digits and thus all fit within the integer’s max value) and we use unique=True as no two books can have the same ISBN, which is then enforced at the database level.
All objects have a method __str__(self) that defines their string representation. We override the default implementation provided by the models.Model class and instead represent books as “title by author” in all places where the model would be represented as a string. Recall that previously we used list_display in Book‘s admin object to determine what fields would be shown in the admin panel’s list. If that list_display is not present, the admin list instead shows the string representation of the model, as it does for both Patron and Copy.
Finally, we have a method on Book that we called in its admin action that we wrote earlier. This function creates a Copy that is related to a given instance of a Book in the database.
Moving on to Patron, this model introduces the concept of a one-to-one relationship, in this case with the built-in User model. Check it out from records/models.py:
from django.db import models
from django.contrib.auth.models import User
class Patron(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
address = models.CharField(max_length=150)
dob = models.DateField()
def __str__(self):
return self.user.username
The user field is not exactly a bijective function. There CAN be a User instance without an associated Patron instance. However, a User CAN NOT be associated with more than one Patron instance, and a Patroncannot exist without exactly one relation to a user. This is enforced at the database level, and is guaranteed by the on_delete=models.CASCADE specification: if a User instance is deleted, an associated Profile will be deleted.
The other fields and __str__(self) function we’ve seen before. It’s worth noting that you can reach through a one-to-one relation to get attributes, in this case user.username, in a model’s functions.
To expand on the usefulness of database relations, let’s turn our attention to Copy from records/models.py:
from django.db import models
class Copy(models.Model):
book = models.ForeignKey(Book, on_delete=models.CASCADE)
out_to = models.ForeignKey(Patron, blank=True, null=True, on_delete=models.SET_NULL)
def __str__(self):
has_copy = "checked in"
if self.out_to:
has_copy = self.out_to.user.username
return self.book.title + " -> " + has_copy
def check_out(self, p):
self.out_to = p
self.save()
def check_in(self):
self.out_to = None
self.save()
Again, we’ve seen most of this before, so let’s focus on the new stuff: models.ForeignKey. A Copy must be of a single Book, but the library may have multiple Copys of each Book. A Book can exist in the database without the library having a Copy in its catalog, but a Copy cannot exist without an underlying Book.
This complex relationship is expressed with the following line:
book = models.ForeignKey(Book, on_delete=models.CASCADE)
The deletion behavior is the same as Patron‘s in reference to User.
The relationship between a Copy and a Patron is slightly different. A Copy may be checked out to up to one Patrons, but each Patron can check out as many Copys as the library lets them. However, this is not a permanent relationship, the Copy is sometimes not checked out. Patrons and Copys exist independently from one another in the database; deleting an instance of one should not delete any instance of the other.
This relationship is still a use case for the foreign key, but with different arguments:
Here, having blank=True allows for forms to accept None as the value for the relation and null=True allows for the column for the Patron relation in Copy‘s table in the database accept null as a value. The delete behavior, which would be triggered on a Copy if a Patron instance was deleted while they had that Copy checked out, is to sever the relation while leaving the Copy intact by setting the Patron field to null.
The same field type, models.ForeignKey, can express very different relationships between objects. The one relation that I could not cleanly fit in the example is a many-to-many field, which is like a one-to-one field, except that, as suggested by its name, each instance can be related to many other instances and every other and each of those can be related back to many others, like how a book could have multiple authors, each of whom have written multiple books.
Migrations
You might be wondering how the database knows what is expressed in the model. In my experience, migrations are one of those things that are pretty straightforward until they aren’t, and then they eat your face. Here’s how to keep your mug intact, for beginners: learn about migrations and how to interact with them, but try to avoid making manual edits to the migration files. If you already know what you’re doing, skip this section and keep up what works for you.
Migrations translate changes in a model to changes in database schema. You don’t have to write them yourself, Django creates them with the python manage.py makemigrations command. You should run this command when you create a new model or edit the fields of an existing model, but there is no need to do so when creating or editing model methods. It’s important to note that migrations exist as a chain, each one references the previous one so that it can make error-free edits to the database schema. Thus, if you’re collaborating on a project, it’s important to keep a single consistent migration history in version control. When there are unapplied migrations, run python manage.py migrate to apply them before running the server.
The example project is distributed with a single migration, records/migrations/0001_initial.py. Again, this is automatically generated code that you shouldn’t have to edit, so I won’t copy it in here, but if you want to get a sense of what’s going on behind the scenes go ahead and take a look at it.
Fixtures
Unlike migrations, fixtures are not a common aspect of Django development. I use them to distribute sample data with articles, and have never used them otherwise. However, because we used one earlier, I feel compelled to introduce the topic.
For once, the official documentation is a little slim on the topic. Overall, what you should know is that fixtures are a way of importing and exporting data from your database in a variety of formats, including JSON, which is what I use. This feature mostly exists to help with things like automated testing, and is not a backup system or way to edit data in a live database. Furthermore, fixtures are not updated with migrations, and if you try to apply a fixture to a database with an incompatible schema it will fail.
To generate a fixture for the entire database, run:
Writing models in Django is a huge topic, and using the admin panel is another. In 3,000 words, I’ve only managed to introduce each. Hopefully, using the admin panel has given you a better interface to explore how models work and relate to each other, leaving you with the confidence to experiment and develop your own relational representations of data.
If you’re looking for an easy place to start, try adding a Librarian model that inherits from User like Profile does. For more of a challenge, try implementing a checkout history for each Copy and/or Patron (there are several ways of accomplishing this one).
Django Highlights is a series introducing important concepts of web development in Django. Each article is written as a stand-alone guide to a facet of Django development intended to help front-end developers and designers reach a deeper understanding of “the other half” of the codebase. These articles are mostly constructed to help you gain an understanding of theory and convention, but contain some code samples which are written in Django 3.0.
Further Reading
You may be interested in the following articles and documentation.
Have you ever tried to remove the background of a video? It’s not that easy.
Unscreen will automatically remove the background of any video 100% automatically, and for free.
We love a double-whammy.
This means you can record any video, upload the clip into Unscreen, use their tool to scrub the background of your video, and then you can add any background element of your own!
All of this, for free.
Soon enough, they’ll have a pro version as well. I’m excited to see what else they will come up with when they already have such an amazing free design tool.
Neumorphism seems like it’s everywhere nowadays, and I know an app that can help you integrate that into your CSS code.
Neumorphism.iois a generator that will create the CSS for this soft UI style that we’ve been seeing everywhere. This makes your job a lot easier and you can experiment quickly to see what you like for your website.
Finally, and arguably the most aesthetically pleasing app of all, is Vangogh.
If you are ever looking for the perfect color palette, look no further.
All you have to do is type in a keyword, it could be an object or an actual color, and it’ll generate a few variations of the perfect color palette for you.
I can literally just sit here and type in a bunch of random words and get lost for hours in these beautiful colors.
I highly recommend you use this app in your next project!
Wrapping things up
I hope you found this list of my favorite design tools at the moment useful!
Let me know which apps were your favorite and what other design tools that you use daily that I missed.