Front End Web Development
Steven Heller’s Font of the Month: Acorn
Read the book, Typographic Firsts
Steven Heller takes a closer look at Beast of England’s new Acorn font family in this latest issue of Font of the Month.
The post Steven Heller’s Font of the Month: Acorn appeared first on I Love Typography.
Save Big on 6 Popular Sans Serifs
Read the book, Typographic Firsts
Looking for a new sans serif typeface to add to your design arsenal? Look no further! We’re currently offering six of our most popular sans serif typefaces at a special sale price.
The post Save Big on 6 Popular Sans Serifs appeared first on I Love Typography.
Fonts for Luxury Brands: part two
Read the book, Typographic Firsts
In part one, we took a look at three typefaces recommend for fashion or luxury brands. Of course, your final decision about font choice will depend on a variey factors, such as the brand's personality, its target audience, and the kind of message it wants to convey. And, although, we tend to typically associate high contrast typefaces with high fashion, they're by no means our only choice.
The post Fonts for Luxury Brands: part two appeared first on I Love Typography.
Steven Heller’s Font of the Month: Hopeless Diamond
Read the book, Typographic Firsts
Steven Heller takes a closer look at Barnbrook Fonts’ bold and angular font family, Hopeless Diamond.
The post Steven Heller’s Font of the Month: Hopeless Diamond appeared first on I Love Typography.
50% off Fonts
Read the book, Typographic Firsts
For a limited time, you can get 50% off a selection of quality fonts, including some of our most popular designs. Whether you’re a graphic designer, marketer, or simply someone who loves typography, this is an opportunity you won’t want to miss.
The post 50% off Fonts appeared first on I Love Typography.
Introducing Mackay: A Powerful Transitional Serif for Screen and Print
Read the book, Typographic Firsts
Typography is an essential element in any design, and choosing the right typeface makes all the difference. That’s where Mackay comes in — a powerful transitional serif designed for both screen and print. Mackay is available in six weights, ranging from a delicate thin to a blustering black, with matching italics for each weight. Mackay […]
The post Introducing Mackay: A Powerful Transitional Serif for Screen and Print appeared first on I Love Typography.
Passkeys: What the Heck and Why?
These things called passkeys sure are making the rounds these days. They were a main attraction at W3C TPAC 2022, gained support in Safari 16, are finding their way into macOS and iOS, and are slated to be the future for password managers like 1Password. They are already supported in Android, and will soon find their way into Chrome OS and Windows in future releases.
Geeky OS security enhancements don’t exactly make big headlines in the front-end community, but it stands to reason that passkeys are going to be a “thing”. And considering how passwords and password apps affect the user experience of things like authentication and form processing, we might want to at least wrap our minds around them, so we know what’s coming.
That’s the point of this article. I’ve been studying and experimenting with passkeys — and the WebAuthn API they are built on top of — for some time now. Let me share what I’ve learned.
Table of contents- Terminology
- What are passkeys?
- How do passkeys replace passwords?
- More about cryptography
- How do we access passkeys?
- The difference between passkeys and WebAuthn
- The process… in a nutshell
- The meat and potatoes
- Some downsides
- Where are things going?
- Resources
Here’s the obligatory section of the terminology you’re going to want to know as we dig in. Like most tech, passkeys are wrought with esoteric verbiage and acronyms that are often roadblocks to understanding. I’ll try to de-mystify several for you here.
- Relying Party: the server you will be authenticating against. We’ll use “server” to imply the Relying Party in this article.
- Client: in our case, the web browser or operating system.
- Authenticator: Software and/or hardware devices that allow generation and storage for public key pairs.
- FIDO: An open standards body that also creates specifications around FIDO credentials.
- WebAuthn: The underlying protocol for passkeys, Also known as a FIDO2 credential or single-device FIDO credentials.
- Passkeys: WebAuthn, but with cloud syncing (also called multi-device FIDO credentials, discoverable credentials, or resident credentials).
- Public Key Cryptography: A generated key pair that includes a private and public key. Depending on the algorithm, it should either be used for signing and verification or encrypting and decrypting. This is also known as asymmetric cryptography.
- RSA: An acronym of the creators’ names, Rivest Shamir and Adel. RSA is an older, but still useful, family of public key cryptography based on factoring primes.
- Elliptic Curve Cryptography (ECC): A newer family of cryptography based on elliptic curves.
- ES256: An elliptic curve public key that uses an ECDSA signing algorithm (PDF) with SHA256 for hashing.
- RS256: Like ES256, but it uses RSA with RSASSA-PKCS1-v1.5 and SHA256.
Before we can talk specifically about passkeys, we need to talk about another protocol called WebAuthn (also known as FIDO2). Passkeys are a specification that is built on top of WebAuthn. WebAuthn allows for public key cryptography to replace passwords. We use some sort of security device, such as a hardware key or Trusted Platform Module (TPM), to create private and public keys.
The public key is for anyone to use. The private key, however, cannot be removed from the device that generated it. This was one of the issues with WebAuthn; if you lose the device, you lose access.
Passkeys solves this by providing a cloud sync of your credentials. In other words, what you generate on your computer can now also be used on your phone (though confusingly, there are single-device credentials too).
Currently, at the time of writing, only iOS, macOS, and Android provide full support for cloud-synced passkeys, and even then, they are limited by the browser being used. Google and Apple provide an interface for syncing via their Google Password Manager and Apple iCloud Keychain services, respectively.
How do passkeys replace passwords?In public key cryptography, you can perform what is known as signing. Signing takes a piece of data and then runs it through a signing algorithm with the private key, where it can then be verified with the public key.
Anyone can generate a public key pair, and it’s not attributable to any person since any person could have generated it in the first place. What makes it useful is that only data signed with the private key can be verified with the public key. That’s the portion that replaces a password — a server stores the public key, and we sign in by verifying that we have the other half (e.g. private key), by signing a random challenge.
As an added benefit, since we’re storing the user’s public keys within a database, there is no longer concern with password breaches affecting millions of users. This reduces phishing, breaches, and a slew of other security issues that our password-dependent world currently faces. If a database is breached, all that’s stored in the user’s public keys, making it virtually useless to an attacker.
No more forgotten emails and their associated passwords, either! The browser will remember which credentials you used for which website — all you need to do is make a couple of clicks, and you’re logged in. You can provide a secondary means of verification to use the passkey, such as biometrics or a pin, but those are still much faster than the passwords of yesteryear.
More about cryptographyPublic key cryptography involves having a private and a public key (known as a key pair). The keys are generated together and have separate uses. For example, the private key is intended to be kept secret, and the public key is intended for whomever you want to exchange messages with.
When it comes to encrypting and decrypting a message, the recipient’s public key is used to encrypt a message so that only the recipient’s private key can decrypt the message. In security parlance, this is known as “providing confidentiality”. However, this doesn’t provide proof that the sender is who they say they are, as anyone can potentially use a public key to send someone an encrypted message.
There are cases where we need to verify that a message did indeed come from its sender. In these cases, we use signing and signature verification to ensure that the sender is who they say they are (also known as authenticity). In public key (also called asymmetric) cryptography, this is generally done by signing the hash of a message, so that only the public key can correctly verify it. The hash and the sender’s private key produce a signature after running it through an algorithm, and then anyone can verify the message came from the sender with the sender’s public key.
How do we access passkeys?To access passkeys, we first need to generate and store them somewhere. Some of this functionality can be provided with an authenticator. An authenticator is any hardware or software-backed device that provides the ability for cryptographic key generation. Think of those one-time passwords you get from Google Authenticator, 1Password, or LastPass, among others.
For example, a software authenticator can use the Trusted Platform Module (TPM) or secure enclave of a device to create credentials. The credentials can be then stored remotely and synced across devices e.g. passkeys. A hardware authenticator would be something like a YubiKey, which can generate and store keys on the device itself.
To access the authenticator, the browser needs to have access to hardware, and for that, we need an interface. The interface we use here is the Client to Authenticator Protocol (CTAP). It allows access to different authenticators over different mechanisms. For example, we can access an authenticator over NFC, USB, and Bluetooth by utilizing CTAP.
One of the more interesting ways to use passkeys is by connecting your phone over Bluetooth to another device that might not support passkeys. When the devices are paired over Bluetooth, I can log into the browser on my computer using my phone as an intermediary!
The difference between passkeys and WebAuthnPasskeys and WebAuthn keys differ in several ways. First, passkeys are considered multi-device credentials and can be synced across devices. By contrast, WebAuthn keys are single-device credentials — a fancy way of saying you’re bound to one device for verification.
Second, to authenticate to a server, WebAuthn keys need to provide the user handle for login, after which an allowCredentials list is returned to the client from the server, which informs what credentials can be used to log in. Passkeys skip this step and use the server’s domain name to show which keys are already bound to that site. You’re able to select the passkey that is associated with that server, as it’s already known by your system.
Otherwise, the keys are cryptographically the same; they only differ in how they’re stored and what information they use to start the login process.
The process… in a nutshellThe process for generating a WebAuthn or a passkey is very similar: get a challenge from the server and then use the navigator.credentials.create web API to generate a public key pair. Then, send the challenge and the public key back to the server to be stored.
Upon receiving the public key and challenge, the server validates the challenge and the session from which it was created. If that checks out, the public key is stored, as well as any other relevant information like the user identifier or attestation data, in the database.
The user has one more step — retrieve another challenge from the server and use the navigator.credentials.get API to sign the challenge. We send back the signed challenge to the server, and the server verifies the challenge, then logs us in if the signature passes.
There is, of course, quite a bit more to each step. But that is generally how we’d log into a website using WebAuthn or passkeys.
The meat and potatoesPasskeys are used in two distinct phases: the attestation and assertion phases.
The attestation phase can also be thought of as the registration phase. You’d sign up with an email and password for a new website, however, in this case, we’d be using our passkey.
The assertion phase is similar to how you’d log in to a website after signing up.
Attestation View full sizeThe navigator.credentials.create API is the focus of our attestation phase. We’re registered as a new user in the system and need to generate a new public key pair. However, we need to specify what kind of key pair we want to generate. That means we need to provide options to navigator.credentials.create.
// The `challenge` is random and has to come from the server const publicKey: PublicKeyCredentialCreationOptions = { challenge: safeEncode(challenge), rp: { id: window.location.host, name: document.title, }, user: { id: new TextEncoder().encode(crypto.randomUUID()), // Why not make it random? name: 'Your username', displayName: 'Display name in browser', }, pubKeyCredParams: [ { type: 'public-key', alg: -7, // ES256 }, { type: 'public-key', alg: -256, // RS256 }, ], authenticatorSelection: { userVerification: 'preferred', // Do you want to use biometrics or a pin? residentKey: 'required', // Create a resident key e.g. passkey }, attestation: 'indirect', // indirect, direct, or none timeout: 60_000, }; const pubKeyCredential: PublicKeyCredential = await navigator.credentials.create({ publicKey }); const { id // the key id a.k.a. kid } = pubKeyCredential; const pubKey = pubKeyCredential.response.getPublicKey(); const { clientDataJSON, attestationObject } = pubKeyCredential.response; const { type, challenge, origin } = JSON.parse(new TextDecoder().decode(clientDataJSON)); // Send data off to the server for registrationWe’ll get PublicKeyCredential which contains an AuthenticatorAttestationResponse that comes back after creation. The credential has the generated key pair’s ID.
The response provides a couple of bits of useful information. First, we have our public key in this response, and we need to send that to the server to be stored. Second, we also get back the clientDataJSON property which we can decode, and from there, get back the type, challenge, and origin of the passkey.
For attestation, we want to validate the type, challenge, and origin on the server, as well as store the public key with its identifier, e.g. kid. We can also optionally store the attestationObject if we wish. Another useful property to store is the COSE algorithm, which is defined above in our PublicKeyCredentialCreationOptions with alg: -7 or alg: -256, in order to easily verify any signed challenges in the assertion phase.
Assertion View full sizeThe navigator.credentials.get API will be the focus of the assertion phase. Conceptually, this would be where the user logs in to the web application after signing up.
// The `challenge` is random and has to come from the server const publicKey: PublicKeyCredentialRequestOptions = { challenge: new TextEncoder().encode(challenge), rpId: window.location.host, timeout: 60_000, }; const publicKeyCredential: PublicKeyCredential = await navigator.credentials.get({ publicKey, mediation: 'optional', }); const { id // the key id, aka kid } = pubKeyCredential; const { clientDataJSON, attestationObject, signature, userHandle } = pubKeyCredential.response; const { type, challenge, origin } = JSON.parse(new TextDecoder().decode(clientDataJSON)); // Send data off to the server for verificationWe’ll again get a PublicKeyCredential with an AuthenticatorAssertionResponse this time. The credential again includes the key identifier.
We also get the type, challenge, and origin from the clientDataJSON again. The signature is now included in the response, as well as the authenticatorData. We’ll need those and the clientDataJSON to verify that this was signed with the private key.
The authenticatorData includes some properties that are worth tracking First is the SHA256 hash of the origin you’re using, located within the first 32 bytes, which is useful for verifying that request comes from the same origin server. Second is the signCount, which is from byte 33 to 37. This is generated from the authenticator and should be compared to its previous value to ensure that nothing fishy is going on with the key. The value should always 0 when it’s a multi-device passkey and should be randomly larger than the previous signCount when it’s a single-device passkey.
Once you’ve asserted your login, you should be logged in — congratulations! Passkeys is a pretty great protocol, but it does come with some caveats.
Some downsidesThere’s a lot of upside to Passkeys, however, there are some issues with it at the time of this writing. For one thing, passkeys is somewhat still early support-wise, with only single-device credentials allowed on Windows and very little support for Linux systems. Passkeys.dev provides a nice table that’s sort of like the Caniuse of this protocol.
Also, Google’s and Apple’s passkeys platforms do not communicate with each other. If you want to get your credentials from your Android phone over to your iPhone… well, you’re out of luck for now. That’s not to say there is no interoperability! You can log in to your computer by using your phone as an authenticator. But it would be much cleaner just to have it built into the operating system and synced without it being locked at the vendor level.
Where are things going?What does the passkeys protocol of the future look like? It looks pretty good! Once it gains support from more operating systems, there should be an uptake in usage, and you’ll start seeing it used more and more in the wild. Some password managers are even going to support them first-hand.
Passkeys are by no means only supported on the web. Android and iOS will both support native passkeys as first-class citizens. We’re still in the early days of all this, but expect to see it mentioned more and more.
After all, we eliminate the need for passwords, and by doing so, make the world safer for it!
ResourcesHere are some more resources if you want to learn more about Passkeys. There’s also a repository and demo I put together for this article.
- Live Demo (no actual information is collected by the form)
- Demo GitHub Repository
- YubiKey Documentation
- Passkeys.dev
- Passkeys.io
- Webauthn.io
Passkeys: What the Heck and Why? originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
Steven Heller’s Font of the Month: Ray Gun
Read the book, Typographic Firsts
Steven Heller takes a closer look at Derek Weathersbee’s retro-futuristic font, Ray Gun.
The post Steven Heller’s Font of the Month: Ray Gun appeared first on I Love Typography.
Fonts for Luxury Brands
Read the book, Typographic Firsts
We’ve talked about fonts for fashion here on the blog before. And there’s a big overlap between the kinds of fonts used in fashion (especially the high-end stuff) and those used for other luxury brands. It’s also worth mentioning that there’s a recent trend towards simple and unfussy sans serifs even for well-established luxury brands […]
The post Fonts for Luxury Brands appeared first on I Love Typography.
Hot New Fonts: spring 2023
Read the book, Typographic Firsts
We’re always excited to tell you about the launch of great new additions to the ILT Font Store. Here are some recent releases we thought you’d like to hear about. And don’t forget they’re all available as webfonts too. Time to meet the new kids on the block:
The post Hot New Fonts: spring 2023 appeared first on I Love Typography.
Tabrizi Jali typeface: reviving history through letterforms
Read the book, Typographic Firsts
Tabrizi Jali is the first type revival based on the early Persian Naskh types of Iranian presses from the nineteenth century. It is a product of several years of extensive research into the history of printing and typefounding, and practical experiments. Thus, it is a great pleasure to announce its release on the first day of spring and the Persian New Year (Nowruz), the 20th of March, 2023. A year that also marks 200 years since the publication of the earliest known Persian book in Tehran.
The post Tabrizi Jali typeface: reviving history through letterforms appeared first on I Love Typography.
ILT’s Favorite Fonts of 2022
Read the book, Typographic Firsts
It’s that time of year again. Join us as we look back at some of our favorite fonts published on ILT in 2022.
The post ILT’s Favorite Fonts of 2022 appeared first on I Love Typography.
Making Calendars With Accessibility and Internationalization in Mind
Doing a quick search here on CSS-Tricks shows just how many different ways there are to approach calendars. Some show how CSS Grid can create the layout efficiently. Some attempt to bring actual data into the mix. Some rely on a framework to help with state management.
There are many considerations when building a calendar component — far more than what is covered in the articles I linked up. If you think about it, calendars are fraught with nuance, from handling timezones and date formats to localization and even making sure dates flow from one month to the next… and that’s before we even get into accessibility and additional layout considerations depending on where the calendar is displayed and whatnot.
Many developers fear the Date() object and stick with older libraries like moment.js. But while there are many “gotchas” when it comes to dates and formatting, JavaScript has a lot of cool APIs and stuff to help out!
I don’t want to re-create the wheel here, but I will show you how we can get a dang good calendar with vanilla JavaScript. We’ll look into accessibility, using semantic markup and screenreader-friendly <time> -tags — as well as internationalization and formatting, using the Intl.Locale, Intl.DateTimeFormat and Intl.NumberFormat-APIs.
In other words, we’re making a calendar… only without the extra dependencies you might typically see used in a tutorial like this, and with some of the nuances you might not typically see. And, in the process, I hope you’ll gain a new appreciation for newer things that JavaScript can do while getting an idea of the sorts of things that cross my mind when I’m putting something like this together.
First off, namingWhat should we call our calendar component? In my native language, it would be called “kalender element”, so let’s use that and shorten that to “Kal-El” — also known as Superman’s name on the planet Krypton.
Let’s create a function to get things going:
function kalEl(settings = {}) { ... }This method will render a single month. Later we’ll call this method from [...Array(12).keys()] to render an entire year.
Initial data and internationalizationOne of the common things a typical online calendar does is highlight the current date. So let’s create a reference for that:
const today = new Date();Next, we’ll create a “configuration object” that we’ll merge with the optional settings object of the primary method:
const config = Object.assign( { locale: (document.documentElement.getAttribute('lang') || 'en-US'), today: { day: today.getDate(), month: today.getMonth(), year: today.getFullYear() } }, settings );We check, if the root element (<html>) contains a lang-attribute with locale info; otherwise, we’ll fallback to using en-US. This is the first step toward internationalizing the calendar.
We also need to determine which month to initially display when the calendar is rendered. That’s why we extended the config object with the primary date. This way, if no date is provided in the settings object, we’ll use the today reference instead:
const date = config.date ? new Date(config.date) : today;We need a little more info to properly format the calendar based on locale. For example, we might not know whether the first day of the week is Sunday or Monday, depending on the locale. If we have the info, great! But if not, we’ll update it using the Intl.Locale API. The API has a weekInfo object that returns a firstDay property that gives us exactly what we’re looking for without any hassle. We can also get which days of the week are assigned to the weekend:
if (!config.info) config.info = new Intl.Locale(config.locale).weekInfo || { firstDay: 7, weekend: [6, 7] };Again, we create fallbacks. The “first day” of the week for en-US is Sunday, so it defaults to a value of 7. This is a little confusing, as the getDay method in JavaScript returns the days as [0-6], where 0 is Sunday… don’t ask me why. The weekends are Saturday and Sunday, hence [6, 7].
Before we had the Intl.Locale API and its weekInfo method, it was pretty hard to create an international calendar without many **objects and arrays with information about each locale or region. Nowadays, it’s easy-peasy. If we pass in en-GB, the method returns:
// en-GB { firstDay: 1, weekend: [6, 7], minimalDays: 4 }In a country like Brunei (ms-BN), the weekend is Friday and Sunday:
// ms-BN { firstDay: 7, weekend: [5, 7], minimalDays: 1 }You might wonder what that minimalDays property is. That’s the fewest days required in the first week of a month to be counted as a full week. In some regions, it might be just one day. For others, it might be a full seven days.
Next, we’ll create a render method within our kalEl-method:
const render = (date, locale) => { ... }We still need some more data to work with before we render anything:
const month = date.getMonth(); const year = date.getFullYear(); const numOfDays = new Date(year, month + 1, 0).getDate(); const renderToday = (year === config.today.year) && (month === config.today.month);The last one is a Boolean that checks whether today exists in the month we’re about to render.
Semantic markupWe’re going to get deeper in rendering in just a moment. But first, I want to make sure that the details we set up have semantic HTML tags associated with them. Setting that up right out of the box gives us accessibility benefits from the start.
Calendar wrapperFirst, we have the non-semantic wrapper: <kal-el>. That’s fine because there isn’t a semantic <calendar> tag or anything like that. If we weren’t making a custom element, <article> might be the most appropriate element since the calendar could stand on its own page.
Month namesThe <time> element is going to be a big one for us because it helps translate dates into a format that screenreaders and search engines can parse more accurately and consistently. For example, here’s how we can convey “January 2023” in our markup:
<time datetime="2023-01">January <i>2023</i></time> Day namesThe row above the calendar’s dates containing the names of the days of the week can be tricky. It’s ideal if we can write out the full names for each day — e.g. Sunday, Monday, Tuesday, etc. — but that can take up a lot of space. So, let’s abbreviate the names for now inside of an <ol> where each day is a <li>:
<ol> <li><abbr title="Sunday">Sun</abbr></li> <li><abbr title="Monday">Mon</abbr></li> <!-- etc. --> </ol>We could get tricky with CSS to get the best of both worlds. For example, if we modified the markup a bit like this:
<ol> <li> <abbr title="S">Sunday</abbr> </li> </ol>…we get the full names by default. We can then “hide” the full name when space runs out and display the title attribute instead:
@media all and (max-width: 800px) { li abbr::after { content: attr(title); } }But, we’re not going that way because the Intl.DateTimeFormat API can help here as well. We’ll get to that in the next section when we cover rendering.
Day numbersEach date in the calendar grid gets a number. Each number is a list item (<li>) in an ordered list (<ol>), and the inline <time> tag wraps the actual number.
<li> <time datetime="2023-01-01">1</time> </li>And while I’m not planning to do any styling just yet, I know I will want some way to style the date numbers. That’s possible as-is, but I also want to be able to style weekday numbers differently than weekend numbers if I need to. So, I’m going to include data-* attributes specifically for that: data-weekend and data-today.
Week numbersThere are 52 weeks in a year, sometimes 53. While it’s not super common, it can be nice to display the number for a given week in the calendar for additional context. I like having it now, even if I don’t wind up not using it. But we’ll totally use it in this tutorial.
We’ll use a data-weeknumber attribute as a styling hook and include it in the markup for each date that is the week’s first date.
<li data-day="7" data-weeknumber="1" data-weekend=""> <time datetime="2023-01-08">8</time> </li> RenderingLet’s get the calendar on a page! We already know that <kal-el> is the name of our custom element. First thing we need to configure it is to set the firstDay property on it, so the calendar knows whether Sunday or some other day is the first day of the week.
<kal-el data-firstday="${ config.info.firstDay }">We’ll be using template literals to render the markup. To format the dates for an international audience, we’ll use the Intl.DateTimeFormat API, again using the locale we specified earlier.
The month and yearWhen we call the month, we can set whether we want to use the long name (e.g. February) or the short name (e.g. Feb.). Let’s use the long name since it’s the title above the calendar:
<time datetime="${year}-${(pad(month))}"> ${new Intl.DateTimeFormat( locale, { month:'long'}).format(date)} <i>${year}</i> </time> Weekday namesFor weekdays displayed above the grid of dates, we need both the long (e.g. “Sunday”) and short (abbreviated, ie. “Sun”) names. This way, we can use the “short” name when the calendar is short on space:
Intl.DateTimeFormat([locale], { weekday: 'long' }) Intl.DateTimeFormat([locale], { weekday: 'short' })Let’s make a small helper method that makes it a little easier to call each one:
const weekdays = (firstDay, locale) => { const date = new Date(0); const arr = [...Array(7).keys()].map(i => { date.setDate(5 + i) return { long: new Intl.DateTimeFormat([locale], { weekday: 'long'}).format(date), short: new Intl.DateTimeFormat([locale], { weekday: 'short'}).format(date) } }) for (let i = 0; i < 8 - firstDay; i++) arr.splice(0, 0, arr.pop()); return arr; }Here’s how we invoke that in the template:
<ol> ${weekdays(config.info.firstDay,locale).map(name => ` <li> <abbr title="${name.long}">${name.short}</abbr> </li>`).join('') } </ol> Day numbersAnd finally, the days, wrapped in an <ol> element:
${[...Array(numOfDays).keys()].map(i => { const cur = new Date(year, month, i + 1); let day = cur.getDay(); if (day === 0) day = 7; const today = renderToday && (config.today.day === i + 1) ? ' data-today':''; return ` <li data-day="${day}"${today}${i === 0 || day === config.info.firstDay ? ` data-weeknumber="${new Intl.NumberFormat(locale).format(getWeek(cur))}"`:''}${config.info.weekend.includes(day) ? ` data-weekend`:''}> <time datetime="${year}-${(pad(month))}-${pad(i)}" tabindex="0"> ${new Intl.NumberFormat(locale).format(i + 1)} </time> </li>` }).join('')}Let’s break that down:
- We create a “dummy” array, based on the “number of days” variable, which we’ll use to iterate.
- We create a day variable for the current day in the iteration.
- We fix the discrepancy between the Intl.Locale API and getDay().
- If the day is equal to today, we add a data-* attribute.
- Finally, we return the <li> element as a string with merged data.
- tabindex="0" makes the element focusable, when using keyboard navigation, after any positive tabindex values (Note: you should never add positive tabindex-values)
To “pad” the numbers in the datetime attribute, we use a little helper method:
const pad = (val) => (val + 1).toString().padStart(2, '0'); Week numberAgain, the “week number” is where a week falls in a 52-week calendar. We use a little helper method for that as well:
function getWeek(cur) { const date = new Date(cur.getTime()); date.setHours(0, 0, 0, 0); date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7); const week = new Date(date.getFullYear(), 0, 4); return 1 + Math.round(((date.getTime() - week.getTime()) / 86400000 - 3 + (week.getDay() + 6) % 7) / 7); }I didn’t write this getWeek-method. It’s a cleaned up version of this script.
And that’s it! Thanks to the Intl.Locale, Intl.DateTimeFormat and Intl.NumberFormat APIs, we can now simply change the lang-attribute of the <html> element to change the context of the calendar based on the current region:
de-DE fa-IR zh-Hans-CN-u-nu-hanidec Styling the calendarYou might recall how all the days are just one <ol> with list items. To style these into a readable calendar, we dive into the wonderful world of CSS Grid. In fact, we can repurpose the same grid from a starter calendar template right here on CSS-Tricks, but updated a smidge with the :is() relational pseudo to optimize the code.
Notice that I’m defining configurable CSS variables along the way (and prefixing them with ---kalel- to avoid conflicts).
kal-el :is(ol, ul) { display: grid; font-size: var(--kalel-fz, small); grid-row-gap: var(--kalel-row-gap, .33em); grid-template-columns: var(--kalel-gtc, repeat(7, 1fr)); list-style: none; margin: unset; padding: unset; position: relative; }Let’s draw borders around the date numbers to help separate them visually:
kal-el :is(ol, ul) li { border-color: var(--kalel-li-bdc, hsl(0, 0%, 80%)); border-style: var(--kalel-li-bds, solid); border-width: var(--kalel-li-bdw, 0 0 1px 0); grid-column: var(--kalel-li-gc, initial); text-align: var(--kalel-li-tal, end); }The seven-column grid works fine when the first day of the month is also the first day of the week for the selected locale). But that’s the exception rather than the rule. Most times, we’ll need to shift the first day of the month to a different weekday.
Remember all the extra data-* attributes we defined when writing our markup? We can hook into those to update which grid column (--kalel-li-gc) the first date number of the month is placed on:
[data-firstday="1"] [data-day="3"]:first-child { --kalel-li-gc: 1 / 4; }In this case, we’re spanning from the first grid column to the fourth grid column — which will automatically “push” the next item (Day 2) to the fifth grid column, and so forth.
Let’s add a little style to the “current” date, so it stands out. These are just my styles. You can totally do what you’d like here.
[data-today] { --kalel-day-bdrs: 50%; --kalel-day-bg: hsl(0, 86%, 40%); --kalel-day-hover-bgc: hsl(0, 86%, 70%); --kalel-day-c: #fff; }I like the idea of styling the date numbers for weekends differently than weekdays. I’m going to use a reddish color to style those. Note that we can reach for the :not() pseudo-class to select them while leaving the current date alone:
[data-weekend]:not([data-today]) { --kalel-day-c: var(--kalel-weekend-c, hsl(0, 86%, 46%)); }Oh, and let’s not forget the week numbers that go before the first date number of each week. We used a data-weeknumber attribute in the markup for that, but the numbers won’t actually display unless we reveal them with CSS, which we can do on the ::before pseudo-element:
[data-weeknumber]::before { display: var(--kalel-weeknumber-d, inline-block); content: attr(data-weeknumber); position: absolute; inset-inline-start: 0; /* additional styles */ }We’re technically done at this point! We can render a calendar grid that shows the dates for the current month, complete with considerations for localizing the data by locale, and ensuring that the calendar uses proper semantics. And all we used was vanilla JavaScript and CSS!
But let’s take this one more step…
Rendering an entire yearMaybe you need to display a full year of dates! So, rather than render the current month, you might want to display all of the month grids for the current year.
Well, the nice thing about the approach we’re using is that we can call the render method as many times as we want and merely change the integer that identifies the month on each instance. Let’s call it 12 times based on the current year.
as simple as calling the render-method 12 times, and just change the integer for month — i:
[...Array(12).keys()].map(i => render( new Date(date.getFullYear(), i, date.getDate()), config.locale, date.getMonth() ) ).join('')It’s probably a good idea to create a new parent wrapper for the rendered year. Each calendar grid is a <kal-el> element. Let’s call the new parent wrapper <jor-el>, where Jor-El is the name of Kal-El’s father.
<jor-el id="app" data-year="true"> <kal-el data-firstday="7"> <!-- etc. --> </kal-el> <!-- other months --> </jor-el>We can use <jor-el> to create a grid for our grids. So meta!
jor-el { background: var(--jorel-bg, none); display: var(--jorel-d, grid); gap: var(--jorel-gap, 2.5rem); grid-template-columns: var(--jorel-gtc, repeat(auto-fill, minmax(320px, 1fr))); padding: var(--jorel-p, 0); } Final demo CodePen Embed Fallback Bonus: Confetti CalendarI read an excellent book called Making and Breaking the Grid the other day and stumbled on this beautiful “New Year’s poster”:
Source: Making and Breaking the Grid (2nd Edition) by Timothy SamaraI figured we could do something similar without changing anything in the HTML or JavaScript. I’ve taken the liberty to include full names for months, and numbers instead of day names, to make it more readable. Enjoy!
CodePen Embed FallbackMaking Calendars With Accessibility and Internationalization in Mind originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
5 Mistakes I Made When Starting My First React Project
You know what it’s like to pick up a new language or framework. Sometimes there’s great documentation to help you find your way through it. But even the best documentation doesn’t cover absolutely everything. And when you work with something that’s new, you’re bound to find a problem that doesn’t have a written solution.
That’s how it was for me the first time I created a React project — and React is one of those frameworks with remarkable documentation, especially now with the beta docs. But I still struggled my way through. It’s been quite a while since that project, but the lessons I gained from it are still fresh in my mind. And even though there are a lot of React “how-to” tutorials in out there, I thought I’d share what I wish I knew when I first used it.
So, that’s what this article is — a list of the early mistakes I made. I hope they help make learning React a lot smoother for you.
Using create-react-app to start a projectTL;DR Use Vite or Parcel.
Create React App (CRA) is a tool that helps you set up a new React project. It creates a development environment with the best configuration options for most React projects. This means you don’t have to spend time configuring anything yourself.
As a beginner, this seemed like a great way to start my work! No configuration! Just start coding!
CRA uses two popular packages to achieve this, webpack and Babel. webpack is a web bundler that optimizes all of the assets in your project, such as JavaScript, CSS, and images. Babel is a tool that allows you to use newer JavaScript features, even if some browsers don’t support them.
Both are good, but there are newer tools that can do the job better, specifically Vite and Speedy Web Compiler (SWC).
These new and improved alternatives are faster and easier to configure than webpack and Babel. This makes it easier to adjust the configuration which is difficult to do in create-react-app without ejecting.
To use them both when setting up a new React project you have to make sure you have Node version 12 or higher installed, then run the following command.
npm create viteYou’ll be asked to pick a name for your project. Once you do that, select React from the list of frameworks. After that, you can select either Javascript + SWC or Typescript + SWC
Then you’ll have to change directory cd into your project and run the following command;
npm i && npm run devThis should run a development server for your site with the URL localhost:5173
And it’s as simple as that.
Article on Jan 11, 2022 Adding Vite to Your Existing Web App learning react Adam Rackis Article on Jan 18, 2022 Making a Site Work Offline Using the VitePWA Plugin learning react Adam Rackis Article on Jan 12, 2022 Parcel CSS: A New CSS Parser, Transformer, and Minifier learning react Chris Coyier Article on Apr 25, 2019 Using Parcel as a Bundler for React Applications learning react Kingsley Silas Using defaultProps for default valuesTL;DR Use default function parameters instead.
Data can be passed to React components through something called props. These are added to a component just like attributes in an HTML element and can be used in a component’s definition by taking the relevant values from the prop object passed in as an argument.
// App.jsx export default function App() { return <Card title="Hello" description="world" /> } // Card.jsx function Card(props) { return ( <div> <h1>{props.title}</h1> <p>{props.description}</p> </div> ); } export default Card;If a default value is ever required for a prop, the defaultProp property can be used:
// Card.jsx function Card(props) { // ... } Card.defaultProps = { title: 'Default title', description: 'Desc', }; export default Card;With modern JavaScript, it is possible to destructure the props object and assign a default value to it all in the function argument.
// Card.jsx function Card({title = "Default title", description= "Desc"}) { return ( <div> <h1>{title}</h1> <p>{description}</p> </div> ) } export default Card;This is more favorable as the code that can be read by modern browsers without the need for extra transformation.
Unfortunately, defaultProps do require some transformation to be read by the browser since JSX (JavaScript XML) isn’t supported out of the box. This could potentially affect the performance of an application that is using a lot of defaultProps.
Article on Oct 23, 2019 Demonstrating Reusable React Components in a Form learning react Kingsley Silas Article on Jun 7, 2017 I Learned How to be Productive in React in a Week and You Can, Too learning react Sarah Drasner Article on Aug 31, 2018 Props and PropTypes in React learning react Kingsley Silas Don’t use propTypesTL;DR Use TypeScript.
In React, the propTypes property can be used to check if a component is being passed the correct data type for its props. They allow you to specify the type of data that should be used for each prop such as a string, number, object, etc. They also allow you to specify if a prop is required or not.
This way, if a component is passed the wrong data type or if a required prop is not being provided, then React will throw an error.
// Card.jsx import { PropTypes } from "prop-types"; function Card(props) { // ... } Card.propTypes = { title: PropTypes.string.isRequired, description: PropTypes.string, }; export default Card;TypeScript provides a level of type safety in data that’s being passed to components. So, sure, propTypes were a good idea back when I was starting. However, now that TypeScript has become the go-to solution for type safety, I would highly recommend using it over anything else.
// Card.tsx interface CardProps { title: string, description?: string, } export default function Card(props: CardProps) { // ... }TypeScript is a programming language that builds on top of JavaScript by adding static type-checking. TypeScript provides a more powerful type system, that can catch more potential bugs and improves the development experience.
Article on Aug 31, 2018 Props and PropTypes in React learning react Kingsley Silas Article on Mar 27, 2018 Putting Things in Context With React learning react Neal Fennimore Article on Nov 16, 2018 An Overview of Render Props in React learning react Kingsley Silas Using class componentsTL;DR: Write components as functions
Class components in React are created using JavaScript classes. They have a more object-oriented structure and as well as a few additional features, like the ability to use the this keyword and lifecycle methods.
// Card.jsx class Card extends React.Component { render() { return ( <div> <h1>{this.props.title}</h1> <p>{this.props.description}</p> </div> ) } } export default Card;I prefer writing components with classes over functions, but JavaScript classes are more difficult for beginners to understand and this can get very confusing. Instead, I’d recommend writing components as functions:
// Card.jsx function Card(props) { return ( <div> <h1>{props.title}</h1> <p>{props.description}</p> </div> ) } export default Card;Function components are simply JavaScript functions that return JSX. They are much easier to read, and do not have additional features like the this keyword and lifecycle methods which make them more performant than class components.
Function components also have the advantage of using hooks. React Hooks allow you to use state and other React features without writing a class component, making your code more readable, maintainable and reusable.
Article on Jul 6, 2019 Getting to Know the useReducer React Hook learning react Kingsley Silas Article on May 1, 2020 Intro to React Hooks learning react Kingsley Silas Article on Jul 15, 2022 React Hooks: The Deep Cuts learning react Blessing Ene Anyebe Importing React unnecessarilyTL;DR: There’s no need to do it, unless you need hooks.
Since React 17 was released in 2020, it’s now unnecessary to import React at the top of your file whenever you create a component.
import React from 'react'; // Not needed! export default function Card() {}But we had to do that before React 17 because the JSX transformer (the thing that converts JSX into regular JavaScript) used a method called React.createElement that would only work when importing React. Since then, a new transformer has been release which can transform JSX without the createElement method.
You will still need to import React to use hooks, fragments, and any other functions or components you might need from the library:
import { useState } from 'react'; export default function Card() { const [count, setCount] = useState(0); // ... } Those were my early mistakes!Maybe “mistake” is too harsh a word since some of the better practices came about later. Still, I see plenty of instances where the “old” way of doing something is still being actively used in projects and other tutorials.
To be honest, I probably made way more than five mistakes when getting started. Anytime you reach for a new tool it is going to be more like a learning journey to use it effectively, rather than flipping a switch. But these are the things I still carry with me years later!
If you’ve been using React for a while, what are some of the things you wish you knew before you started? It would be great to get a collection going to help others avoid the same struggles.
5 Mistakes I Made When Starting My First React Project originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
Power up your design game!
Read the book, Typographic Firsts
We’re thrilled to announce new ILT Academy courses and workshops. Take your Glyphs skills to the next level with our Glyphs Power-Up workshop, learn to design a Cyrillic typeface from scratch, or start designing variable fonts.
The post Power up your design game! appeared first on I Love Typography.
Creating a Clock with the New CSS sin() and cos() Trigonometry Functions
CSS trigonometry functions are here! Well, they are if you’re using the latest versions of Firefox and Safari, that is. Having this sort of mathematical power in CSS opens up a whole bunch of possibilities. In this tutorial, I thought we’d dip our toes in the water to get a feel for a couple of the newer functions: sin() and cos().
There are other trigonometry functions in the pipeline — including tan() — so why focus just on sin() and cos()? They happen to be perfect for the idea I have in mind, which is to place text along the edge of a circle. That’s been covered here on CSS-Tricks when Chris shared an approach that uses a Sass mixin. That was six years ago, so let’s give it the bleeding edge treatment.
Here’s what I have in mind. Again, it’s only supported in Firefox and Safari at the moment:
CodePen Embed FallbackSo, it’s not exactly like words forming a circular shape, but we are placing text characters along the circle to form a clock face. Here’s some markup we can use to kick things off:
<div class="clock"> <div class="clock-face"> <time datetime="12:00">12</time> <time datetime="1:00">1</time> <time datetime="2:00">2</time> <time datetime="3:00">3</time> <time datetime="4:00">4</time> <time datetime="5:00">5</time> <time datetime="6:00">6</time> <time datetime="7:00">7</time> <time datetime="8:00">8</time> <time datetime="9:00">9</time> <time datetime="10:00">10</time> <time datetime="11:00">11</time> </div> </div>Next, here are some super basic styles for the .clock-face container. I decided to use the <time> tag with a datetime attribute.
.clock { --_ow: clamp(5rem, 60vw, 40rem); --_w: 88cqi; aspect-ratio: 1; background-color: tomato; border-radius: 50%; container-type: inline; display: grid; height: var(--_ow); place-content: center; position: relative; width var(--_ow); }I decorated things a bit in there, but only to get the basic shape and background color to help us see what we’re doing. Notice how we save the width value in a CSS variable. We’ll use that later. Not much to look at so far:
It looks like some sort of modern art experiment, right? Let’s introduce a new variable, --_r, to store the circle’s radius, which is equal to half of the circle’s width. This way, if the width (--_w) changes, the radius value (--_r) will also update — thanks to another CSS math function, calc():
.clock { --_w: 300px; --_r: calc(var(--_w) / 2); /* rest of styles */ }Now, a bit of math. A circle is 360 degrees. We have 12 labels on our clock, so want to place the numbers every 30 degrees (360 / 12). In math-land, a circle begins at 3 o’clock, so noon is actually minus 90 degrees from that, which is 270 degrees (360 - 90).
Let’s add another variable, --_d, that we can use to set a degree value for each number on the clock face. We’re going to increment the values by 30 degrees to complete our circle:
.clock time:nth-child(1) { --_d: 270deg; } .clock time:nth-child(2) { --_d: 300deg; } .clock time:nth-child(3) { --_d: 330deg; } .clock time:nth-child(4) { --_d: 0deg; } .clock time:nth-child(5) { --_d: 30deg; } .clock time:nth-child(6) { --_d: 60deg; } .clock time:nth-child(7) { --_d: 90deg; } .clock time:nth-child(8) { --_d: 120deg; } .clock time:nth-child(9) { --_d: 150deg; } .clock time:nth-child(10) { --_d: 180deg; } .clock time:nth-child(11) { --_d: 210deg; } .clock time:nth-child(12) { --_d: 240deg; }OK, now’s the time to get our hands dirty with the sin() and cos() functions! What we want to do is use them to get the X and Y coordinates for each number so we can place them properly around the clock face.
The formula for the X coordinate is radius + (radius * cos(degree)). Let’s plug that into our new --_x variable:
--_x: calc(var(--_r) + (var(--_r) * cos(var(--_d))));The formula for the Y coordinate is radius + (radius * sin(degree)). We have what we need to calculate that:
--_y: calc(var(--_r) + (var(--_r) * sin(var(--_d))));There are a few housekeeping things we need to do to set up the numbers, so let’s put some basic styling on them to make sure they are absolutely positioned and placed with our coordinates:
.clock-face time { --_x: calc(var(--_r) + (var(--_r) * cos(var(--_d)))); --_y: calc(var(--_r) + (var(--_r) * sin(var(--_d)))); --_sz: 12cqi; display: grid; height: var(--_sz); left: var(--_x); place-content: center; position: absolute; top: var(--_y); width: var(--_sz); }Notice --_sz, which we’ll use for the width and height of the numbers in a moment. Let’s see what we have so far.
This definitely looks more like a clock! See how the top-left corner of each number is positioned at the correct place around the circle? We need to “shrink” the radius when calculating the positions for each number. We can deduct the size of a number (--_sz) from the size of the circle (--_w), before we calculate the radius:
--_r: calc((var(--_w) - var(--_sz)) / 2);Much better! Let’s change the colors, so it looks more elegant:
We could stop right here! We accomplished the goal of placing text around a circle, right? But what’s a clock without arms to show hours, minutes, and seconds?
Let’s use a single CSS animation for that. First, let’s add three more elements to our markup,
<div class="clock"> <!-- after <time>-tags --> <span class="arm seconds"></span> <span class="arm minutes"></span> <span class="arm hours"></span> <span class="arm center"></span> </div>Then some common markup for all three arms. Again, most of this is just make sure the arms are absolutely positioned and placed accordingly:
.arm { background-color: var(--_abg); border-radius: calc(var(--_aw) * 2); display: block; height: var(--_ah); left: calc((var(--_w) - var(--_aw)) / 2); position: absolute; top: calc((var(--_w) / 2) - var(--_ah)); transform: rotate(0deg); transform-origin: bottom; width: var(--_aw); }We’ll use the same animation for all three arms:
@keyframes turn { to { transform: rotate(1turn); } }The only difference is the time the individual arms take to make a full turn. For the hours arm, it takes 12 hours to make a full turn. The animation-duration property only accepts values in milliseconds and seconds. Let’s stick with seconds, which is 43,200 seconds (60 seconds * 60 minutes * 12 hours).
animation: turn 43200s infinite;It takes 1 hour for the minutes arm to make a full turn. But we want this to be a multi-step animation so the movement between the arms is staggered rather than linear. We’ll need 60 steps, one for each minute:
animation: turn 3600s steps(60, end) infinite;The seconds arm is almost the same as the minutes arm, but the duration is 60 seconds instead of 60 minutes:
animation: turn 60s steps(60, end) infinite;Let’s update the properties we created in the common styles:
.seconds { --_abg: hsl(0, 5%, 40%); --_ah: 145px; --_aw: 2px; animation: turn 60s steps(60, end) infinite; } .minutes { --_abg: #333; --_ah: 145px; --_aw: 6px; animation: turn 3600s steps(60, end) infinite; } .hours { --_abg: #333; --_ah: 110px; --_aw: 6px; animation: turn 43200s linear infinite; }What if we want to start at the current time? We need a little bit of JavaScript:
const time = new Date(); const hour = -3600 * (time.getHours() % 12); const mins = -60 * time.getMinutes(); app.style.setProperty('--_dm', `${mins}s`); app.style.setProperty('--_dh', `${(hour+mins)}s`);I’ve added id="app" to the clockface and set two new custom properties on it that set a negative animation-delay, as Mate Marschalko did when he shared a CSS-only clock. The getHours() method of JavaScipt’s Date object is using the 24-hour format, so we use the remainder operator to convert it into 12-hour format.
In the CSS, we need to add the animation-delay as well:
.minutes { animation-delay: var(--_dm, 0s); /* other styles */ } .hours { animation-delay: var(--_dh, 0s); /* other styles */ }Just one more thing. Using CSS @supports and the properties we’ve already created, we can provide a fallback to browsers that do not supprt sin() and cos(). (Thank you, Temani Afif!):
@supports not (left: calc(1px * cos(45deg))) { time { left: 50% !important; top: 50% !important; transform: translate(-50%,-50%) rotate(var(--_d)) translate(var(--_r)) rotate(calc(-1*var(--_d))) } }And, voilà! Our clock is done! Here’s the final demo one more time. Again, it’s only supported in Firefox and Safari at the moment.
CodePen Embed Fallback What else can we do?Just messing around here, but we can quickly turn our clock into a circular image gallery by replacing the <time> tags with <img> then updating the width (--_w) and radius (--_r) values:
CodePen Embed FallbackLet’s try one more. I mentioned earlier how the clock looked kind of like a modern art experiment. We can lean into that and re-create a pattern I saw on a poster (that I unfortunately didn’t buy) in an art gallery the other day. As I recall, it was called “Moon” and consisted of a bunch of dots forming a circle.
We’ll use an unordered list this time since the circles don’t follow a particular order. We’re not even going to put all the list items in the markup. Instead, let’s inject them with JavaScript and add a few controls we can use to manipulate the final result.
The controls are range inputs (<input type="range">) which we’ll wrap in a <form> and listen for the input event.
<form id="controls"> <fieldset> <label>Number of rings <input type="range" min="2" max="12" value="10" id="rings" /> </label> <label>Dots per ring <input type="range" min="5" max="12" value="7" id="dots" /> </label> <label>Spread <input type="range" min="10" max="40" value="40" id="spread" /> </label> </fieldset> </form>We’ll run this method on “input”, which will create a bunch of <li> elements with the degree (--_d) variable we used earlier applied to each one. We can also repurpose our radius variable (--_r) .
I also want the dots to be different colors. So, let’s randomize (well, not completely randomized) the HSL color value for each list item and store it as a new CSS variable, --_bgc:
const update = () => { let s = ""; for (let i = 1; i <= rings.valueAsNumber; i++) { const r = spread.valueAsNumber * i; const theta = coords(dots.valueAsNumber * i); for (let j = 0; j < theta.length; j++) { s += `<li style="--_d:${theta[j]};--_r:${r}px;--_bgc:hsl(${random( 50, 25 )},${random(90, 50)}%,${random(90, 60)}%)"></li>`; } } app.innerHTML = s; }The random() method picks a value within a defined range of numbers:
const random = (max, min = 0, f = true) => f ? Math.floor(Math.random() * (max - min) + min) : Math.random() * max;And that’s it. We use JavaScript to render the markup, but as soon as it’s rendered, we don’t really need it. The sin() and cos() functions help us position all the dots in the right spots.
CodePen Embed Fallback Final thoughtsPlacing things around a circle is a pretty basic example to demonstrate the powers of trigonometry functions like sin() and cos(). But it’s really cool that we are getting modern CSS features that provide new solutions for old workarounds I’m sure we’ll see way more interesting, complex, and creative use cases, especially as browser support comes to Chrome and Edge.
Creating a Clock with the New CSS sin() and cos() Trigonometry Functions originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
Steven Heller’s Font of the Month: Juma
Read the book, Typographic Firsts
Steven Heller takes a closer look at Cyla Costa’s bold brilliant and wonderful new color font family, Juma.
The post Steven Heller’s Font of the Month: Juma appeared first on I Love Typography.
Hot New Fonts: Rotulo
Read the book, Typographic Firsts
A quick heads up about a great new release from Huy!Fonts. Rotulo is a huge new sans serif family that comes in 90, yes ninety styles. If you prefer varaible fonts, then this is your lucky day because Rotulo also comes in a single variable font with all three axes — width, weight, and slant — baked in.
The post Hot New Fonts: Rotulo appeared first on I Love Typography.
Expressive script fonts
Read the book, Typographic Firsts
Expressive script fonts are a great way to introduce some energy into your designs. They can be use in standalone words as logos, for packaging, and in editorial design. They're also a great way to change pace as part of a system of typographic hierarchy, as headers or subheads. Here are 3 of our favorite expressive script fonts to get you started.
The post Expressive script fonts appeared first on I Love Typography.
Three Free Fonts
Read the book, Typographic Firsts
A brief post today to introduce you to three quality free fonts. There are plenty of poor quality free fonts out there. Some are missing important characters, others are just poorly drawn, or are bastardized versions of popular font families. But some $0 fonts are great quality and make welcome additions to anyone’s font library. […]
The post Three Free Fonts appeared first on I Love Typography.
