r/webdev - Bundle your React app with Rollup.js
Strategy

Bundle your React app with Rollup.js : webdev


r/webdev - Bundle your React app with Rollup.js

Rollup & React

According to Tooling.Report maintained by GoogleChromeLabs , Rollup.js performs better than other bundlers.

r/webdev - Bundle your React app with Rollup.js

bundlers

In this blog post, we will see how to bundle a React.js app with rollup.js.

Let’s start

Make sure that you have installed a node.js version in your machine. Nvm makes things easier and allows us to have multiple versions of node.js in our machine.

Let’s install the latest stable nodejs version:

nvm install stable 
Now using node v15.3.0 (npm v7.0.14)

Cool! We’re ready to go.

Setup your project:

mkdir react-rollup-app 
cd react-rollup-app 
npm init

The above command will create the initial package.json. We’re going to enhance this file later.

package.json

{
  "name": "react-rollup-app",
  "version": "1.0.0",
  "description": "A react app bundled with rollup.js",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "author": "John Dusaitis",
  "license": "MIT"
}

We’re going to enhance this file later.

Create the html file which will host your React.js app.

index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Your React app bundled with Rollup.js</title>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

Open it with your browser:

r/webdev - Bundle your React app with Rollup.js

loads the page

That’s a good start, but soon or later you’ll need a web server to load other static files such as javascript and css files.

How rollup.js works?

It’s pretty simple:

  1. It takes an input file with your code

  2. It performs some actions to your code based on the provided plugins you gave

  3. It produces one or more output files

Give me a dead simple example

OK, fair! Let’s change the body background colour of our page.

src/index.js

document.body.style.backgroundColor = "yellow";

Install rollup and some plugins for development

Rollup.js comes with a variety of plugins and one of them can play as web server to host all your static files. We will also need another one which reloads the page after each edit of our javascript files.

Install rollup and the above 2 plugins:

npm i rollup rollup-plugin-serve rollup-plugin-livereload –save-dev

Create the rollup configuration file:

rollup.config.dev.js

import serve from 'rollup-plugin-serve';
import livereload from 'rollup-plugin-livereload';
export default {
  input: 'src/index.js',
  plugins: [
    serve({
      open: true,
      verbose: true,
      contentBase: ['', 'dist'],
      historyApiFallback: true,
      host: 'localhost',
      port: 3000
    }),
    livereload({ watch: 'dist' })
  ],
  output: {
    file: 'dist/bundle.js',
    format: 'iife',
    sourcemap: true
  }
};

In our case the output file dist/bunlde.js will be used by our index.html to run the javascript code.

Include it to your index.html file:

index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Your React app bundled with Rollup.js</title>
  </head>
  <body>
    <div id="app"></div>
    <script src="/dist/bundle.js"></script>
  </body>
</html>

Add the build:dev script to package.json

package.json

{
  ...
  "scripts": {
    "build:dev": "rollup -c rollup.config.dev.js -w"
  }
  ...
}

and run the command npm run build:dev.

This command, will load our yellow page to localhost:3000.

r/webdev - Bundle your React app with Rollup.js

Loads the page to localhost:3000

So far, so good. Now it’s time to run some React code.

Setup your React code

Install the react and react-dom libs:

npm i react react-dom

Create your first React component:

src/App.jsx

import React, { useState } from 'react';

const App = () => {
  const [counter, setCounter] = useState(0)
  return (
    <div>
      <h4>A react page bundled with Rollup.js</h4>
      <br />
      <h1>Counter: {counter}</h1>
      <br />
      <button onClick={()=>{setCounter(counter+1)}}>+ Increase</button>
      <br />
      <button onClick={()=>{setCounter(counter-1)}}>- Decrease</button>
    </div>
  );
};


export default App;

Edit the src/index.js to run the react code.

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

const root = document.querySelector('#app');

ReactDOM.render(<App />, root);

Run the build script again:

npm run build:dev

r/webdev - Bundle your React app with Rollup.js

error #1

Hmmm, error No #1. <App />Unexpected token..

This happens because JSX is not valid JavaScript and must be transpiled to valid JavaScript. Babel is one of the most popular JavaScript tranpilers out there.

Babel also allow us to write modern JavaScript. It converts ES6/ES7 to older versions such as ES5 which are compatible with old browsers.

Install babel and the related react-preset.

npm i babel @babel/preset-react @rollup/plugin-babel --save-dev

Edit the rollup.config.dev.js

import serve from 'rollup-plugin-serve';
import livereload from 'rollup-plugin-livereload';
import babel from '@rollup/plugin-babel';

export default {
  input: 'src/index.js',
  plugins: [
    babel({
      presets: ['@babel/preset-react']
    }),
    serve({
      open: true,
      verbose: true,
      contentBase: ['', 'dist'],
      historyApiFallback: true,
      host: 'localhost',
      port: 3000
    }),
    livereload({ watch: 'dist' })
  ],
  output: {
    file: 'dist/bundle.js',
    format: 'iife',
    sourcemap: true
  }
};

Run again the build script:

npm run build:dev

r/webdev - Bundle your React app with Rollup.js

error #2

Error No #2. (!) Unresolved dependencies . The packages react and react-dom cannot be found.

To overcome this issue, we need another rollup plugin. The @rollup/plugin-node-resolve.

npm i @rollup/plugin-node-resolve --save-dev

add it to rollup.config.dev.js:

import serve from 'rollup-plugin-serve';
import livereload from 'rollup-plugin-livereload';
import babel from '@rollup/plugin-babel';
import { nodeResolve } from '@rollup/plugin-node-resolve';

export default {
  input: 'src/index.js',
  plugins: [
    nodeResolve({
       extensions: ['.js', '.jsx']
    }),
    babel({
      presets: ['@babel/preset-react']
    }),
    serve({
      open: true,
      verbose: true,
      contentBase: ['', 'dist'],
      historyApiFallback: true,
      host: 'localhost',
      port: 3000
    }),
    livereload({ watch: 'dist' })
  ],
  output: {
    file: 'dist/bundle.js',
    format: 'iife',
    sourcemap: true
  }
};

Run again the build script:

npm run build:dev

r/webdev - Bundle your React app with Rollup.js

error #3

Error No #3, name exports, this time. Let’s fix it.

For this we need the @rollup/plugin-commonjs plugin which converts CommonJS modules to ES6.

npm i @rollup/plugin-commonjs --save-dev

add it to rollup.config.dev.js:

import serve from 'rollup-plugin-serve';
import livereload from 'rollup-plugin-livereload';
import babel from '@rollup/plugin-babel';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';

export default {
  input: 'src/index.js',
  plugins: [
    nodeResolve({
       extensions: ['.js', '.jsx']
    }),
    babel({
      presets: ['@babel/preset-react']
    }),
    commonjs({
      include: ['node_modules/**']
    }),
    serve({
      open: true,
      verbose: true,
      contentBase: ['', 'dist'],
      historyApiFallback: true,
      host: 'localhost',
      port: 3000
    }),
    livereload({ watch: 'dist' })
  ],
  output: {
    file: 'dist/bundle.js',
    format: 'iife',
    sourcemap: true
  }
};

Let’s run the build script again:

npm run build:dev

r/webdev - Bundle your React app with Rollup.js

created the bundle.js successfully

The build was successful. But, there’s a last error in the page.

r/webdev - Bundle your React app with Rollup.js

error #4

Error No #4, Uncaught ReferenceError: process is not defined. In order to fix this issue, we need a last plugin. The @rollup/plugin-replace which replaces strings in files while bundling.

npm i @rollup/plugin-replace --save-dev

Include it to the rollup config file:

rollup.config.dev.js

import serve from 'rollup-plugin-serve';
import livereload from 'rollup-plugin-livereload';
import babel from '@rollup/plugin-babel';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import replace from '@rollup/plugin-replace';

export default {
  input: 'src/index.js',
  plugins: [
    replace({
      'process.env.NODE_ENV': JSON.stringify('development')
    }),
    nodeResolve({
       extensions: ['.js', '.jsx']
    }),
    babel({
      presets: ['@babel/preset-react']
    }),
    commonjs({
      include: ['node_modules/**']
    }),
    serve({
      open: true,
      verbose: true,
      contentBase: ['', 'dist'],
      historyApiFallback: true,
      host: 'localhost',
      port: 3000
    }),
    livereload({ watch: 'dist' })
  ],
  output: {
    file: 'dist/bundle.js',
    format: 'iife',
    sourcemap: true
  }
};

Let’s run the build script again:

npm run build:dev

r/webdev - Bundle your React app with Rollup.js

the react app ran successfully

🚀 Congrats! You have your first React & Rollup.js app is up and running. 🚀

Find the full source code at my github repo.

And the live working example at https://dusaitis.co/react-rollup-app/index.html

Thanks for reading.

John Dusaitis

P.S. Original blog post: https://dusaitis.com/bundle-your-react-app-with-rollup-js



Source link

Three Things You Didn’t Know About AVIF
Strategy

Three Things You Didn’t Know About AVIF


AVIF, the file format based on the AV1 video codec, is the latest addition to the next-gen image formats. Early reports and comparisons show good results compared to JPEG and WebP. However, even if browser support is good, AVIF is still on the bleeding edge in regards to encoding and decoding. Encoding, decoding, settings and parameters has been well discussed elsewhere. 

No doubt, AVIF images generate a smaller payload and are nice looking. In this post, we’ll take a closer look at issues to be aware or before you go all in on AVIF. 

1. WebP is Better for Thumbnails

One interesting observation is that for small dimension images, WebP will produce lighter payload images than AVIF.

It’s probably possible to explain why, and tune the encoder to address this case. However, that is not an option for most people. Most people would probably rely on an image optimizer like squoosh.app or an image CDN like ImageEngine. The below comparison uses exactly these two alternatives for AVIF conversion. 

We see that WebP will generally produce images with a higher file size than AVIF. On larger dimension images, ImageEngine performs significantly better than squoosh.app.

Now, to the interesting observation. On images around 100px × 100px squoosh.app passes ImageEngine on effectiveness, but then also WebP catches up and for a 80px x 80px image. WebP is actually the most effective image measured in file size. 

The test performs relatively consistently on different types of images. For this illustration, this image from Picsum is used.

Pixels Original JPEG (bytes) Optimized WebP (bytes) ImageEngine AVIF (bytes) squoosh.app AVIF (bytes)
50 1,475 598 888 687
80 2,090 1,076 1,234 1,070
110 3,022 1,716 1,592 1,580
150 4,457 2,808 2,153 2,275
170 5,300 3,224 2,450 2,670
230 7,792 4,886 3,189 3,900
290 10,895 6,774 4,056 5,130

2. AVIF Might Not Be the Best for Product Images with High Entropy

Typically, a product page consists of images of the product, and when a user’s mouse hovers over or clicks on the product image, it zooms in to offer a closer look at the details.

It is worth noting that AVIF will in certain cases reduce the level of detail, or perceived sharpness, of the image when zoomed in. Especially on a typical product image where the background is blurred or has low entropy while foreground, and product, has more detail and possibly higher entropy.

Below is a zoomed in portion of a bigger product image (JPEG, AVIF) which clearly illustrates the difference between a regularly optimized JPEG versus an AVIF image optimized by squoosh.app.

The AVIF is indeed much lighter than the JPEG, but in this case the trade off between visual quality and lower file size has gone too far. This effect will not be as perceptible for all types of images, and therefore will be difficult to proactively troubleshoot in an automated build process that relies on responsive images syntax for format selection.

Moreover, unlike JPEG, AVIF does not support progressive rendering. For a typical product detail page, progressive rendering might provide a killer feature to improve key metrics like Largest Contentful Paint and other Core Web Vitals metrics. Even if a JPEG takes a little bit longer time to download due to its larger file size compared to AVIF, chances are that it will start rendering sooner than an AVIF thanks to its progressive rendering mechanism. This case is well illustrated by this video from Jake Achibald.

3. JPEG 2000 is Giving AVIF Tough Competition

The key selling point of AVIF is its extremely low file size relative to an acceptable visual image quality. Early blogs and reports have been focusing on this. However, JPEG2000 (or JP2) may in some cases be a better tool for the job. JPEG2000 is a relatively old file format and does not get the same level of attention as AVIF, even if the Apple side of the universe already supports JPEG2000.

To illustrate, let’s look at this adorable puppy. The AVIF file size optimized by squoosh.app is 27.9 KB with default settings. Converting the image to JPEG2000, again using ImageEngine, the file size is 26.7 KB. Not much difference, but enough to illustrate the case.

What about the visual quality? DSSIM is a popular way to compare how visually similar an image is to the original image. The DSSIM metric compares the original image to a converted file, with a lower value indicating better quality. Losslessly converting the AVIF and JPEG2000 version to PNG, the DSSIM score is like this:

DSSIM (0 = equal to original) Bytes
JPEG2000 0.019 26.7 KB
AVIF 0.012 27.9 KB

AVIF has slightly better DSSIM but hardly visible to the human eye.

Right Tool for the Job

The key takeaway from this article is that AVIF is hardly the “silver bullet,” or the one image format to rule them all. First of all, it is still very early in the development of both encoders and decoders. In addition, AVIF is yet another format to manage. Like Jake Archibald also concludes in his article, offering 3+ versions of each image on your webpage is a bit of a pain unless the entire workflow (resize, compress, convert, select, deliver) is all automated.

Also, like we’ve seen, just because a browser supports AVIF, it doesn’t mean that it is the best choice for your users.

Using responsive images and adding AVIF to the list of image formats to pre-create is better than not considering AVIF at all. A potential challenge is that the browser will then pick AVIF if it’s supported regardless of whether AVIF is the right tool or not.

However, using an image CDN like ImageEngine, will to a greater extent be able to dynamically choose between supported formats and make a qualified guess whether WEBP, JPEG2000 or AVIF will give the best user experience. Using an image CDN to automate the image optimization process will take into account browser compatibility, image payload size and visual quality.





Source link

Halfmoon customization using CSS variables
Strategy

Considerations for Making a CSS Framework


Around eight months ago, I started building a framework which would eventually go on to become Halfmoon. I made a post on this very website announcing the launch of the very first version. Halfmoon has been billed as a Bootstrap alternative with a built-in dark mode feature, that is especially good when it comes to building dashboards and tools. All of this still applies to the framework.

However, today I would like to talk about an area of the framework that is a bit understated. I believe our industry as a whole seriously underestimates the value of customization and user personalization, i.e. users being able to set their own design preferences. Chris has written before about knowing who a design system is made for, pointing out a spectrum of flexibility depending on who a system is meant to help.

But it’s more than design systems. Let’s talk about how Halfmoon addresses these issues because they’re important considerations for knowing which framework works best for your specific needs.

Dashboard built using Halfmoon

Who is Halfmoon for?

Before diving in, let’s address an important question: Is Halfmoon the right framework for you? Here’s a list of questions to help you answer that:

  • Are you building a dashboard, tool, or even a documentation website? Halfmoon has many unique components and features that are specific to these use cases.
  • Are you familiar with Bootstrap’s class names, but wish that the design was a bit more premium-looking?
  • Does your users want or expect a dark mode on your website?
  • Do you dislike dependencies? Halfmoon does not use jQuery, and also has no build process involving CSS preprocessors. Everything is pure, vanilla CSS and JavaScript.
  • Are you tired of dealing with complex build systems and front-end tooling? This ties in to the previous point. Personally, I find it difficult to deal with front-end tooling and build processes. As mentioned above, Halfmoon has no build process, so you just pull in the files (local, CDN, or npm), and start building.

If you answered yes to any (or all) of these questions, you should probably give Halfmoon a try. It is important to note however, that Halfmoon is not a UI component library for React/Vue/Angular, so you shouldn’t go into it expecting that. Moreover, if you are more fond of purely utility driven development, then Tailwind CSS is a better option for you. When it comes to CSS utilities, Halfmoon takes a middle of the road approach – there are utilities plus semantic classes for common components.

Using CSS custom properties

First, let’s get the easy stuff out of the way. CSS custom properties are incredible, and I expect them to completely replace preprocessor variables in the future. Browser support is already at a solid ~96%, and with Internet Explorer being phased out by Microsoft, they are expected to become a standard feature.

Halfmoon is built entirely using CSS variables because they provide a huge degree of customization. Now, you might immediately think that all this means is that there are a few custom properties for colors sprinkled in there, but it’s more than that. In fact, there are over 1,500 global variables in Halfmoon. Almost everything can be customized by overriding a property. Here’s a nifty example from the docs:

Halfmoon customization using CSS variables
Swapping out a few custom property values opens up a ton of possibilities in Halfmoon, whether it’s theming things for a brand, or tweaking the UI to get just the right look.

That’s what we’re talking about here when it comes to customization: does the system still stand up and work well if the person using it overrides anything. I have written extensively about this (and much more) in the official Halfmoon docs page.

Variables aren’t a new concept to frameworks. Many frameworks actually use Sass or Less variables and have done so for quite a while. That’s still a good and effective way to establish a customizable experience. But at the same time, those will lock into a preprocessor (which, again, doesn’t have to be a bad thing). By relying instead on CSS custom properties — and variable-izing all the things — we are relying on native CSS, and that doesn’t require any sort of build dependency. So, not only can custom properties make it easier to customize a framework, but they are much more flexible in terms of the tech stack being used.

There is a balance to be had. I know I suggested creating variables for everything, but it can be equally tough to manage and maintain scores and scores of variables (just like anything else in the codebase). So, lean heavily on variables to make a framework or design system more flexible, but also be mindful of how much flexibility you need to provide and whether adding another variable is part of that scope.

Deciding what components to include

When it comes to building a CSS framework, deciding what components to include is a big part of that ordeal. Of course, for a developer working on a passion project, you want to include everything. But that is simply not feasible, so a few decisions were made on my part.

As of right now, Halfmoon has most of the components you can find in similar frameworks such as Bootstrap or Bulma. These frameworks are great and widely used, so they are a good frame of reference. However, as I have mentioned already, a unique thing about Halfmoon is the focus on building tools and dashboards on the web. This niche, if you could call it that, has led me to build some unique components and features:

  • 5 different types of sidebars, with built-in toggle and overlay handlers. Sidebars are very important for most dashboards and tools (and a pain to get right), so this was a no brainer.
  • 2 different types of navbars. There is one that sticks to the bottom of the page, which can be used to great effect for action buttons. Think about the actions that pop up when you select items on data-table. You could place those action buttons here.
  • Omni-directional dropdowns (with 12 different placements, 3 for each direction).
  • Beautiful form components.
  • Built-in keyboard shortcut system, with an easy way to declare new ones for your tool.
  • Tons of utilities. Of course, this is not comparable to Tailwind CSS, but Halfmoon has enough responsive utility classes to handle a lot of use cases right out of the box.

Moreover, the built-in dark mode, huge customizability, and the standard look and feel to the components, should all work together to make Halfmoon a great tool for building web tools and dashboards. And I am hopefully nowhere close to being done! The next updates will bring in a form validator (demo video), more form components, multi-select component, date and time picker, data-table component, etc.

So what is exactly missing from Halfmoon? Well the most obvious ones are tabs, list group, and spinners. But all of these are planned to be added in v1.2.0, which is the next update. There are also other missing components such as carousels, tree navigation, avatars, etc, which are slightly out of scope.

Providing user preferences

Giving end users the ability to set their preferences is often overlooked by frameworks. Things like setting the font size of an article, or whether to use a dark or light theme. In some ways, it’s sort of funny, because the web is catching up to what operating systems have allowed users to do for decades.

Here are some examples of user personalization on the web:

  1. Being able to select your preferred color mode. And, even better, the website automatically saves and respects your preference when the page is loaded. Or better yet, looking at your operating system preferences and automatically accommodating them.
  2. Setting the default size of elements. Especially font size. A small font might look good in a design, but allowing users to set their ideal font size makes the content actually readable. Technically, every modern browser has an option to zoom into content, but that is often unwieldy, and does not actually save your settings.
  3. Setting the compactness of elements. For example, some people prefer large padding with rounded corners, while others find it a waste of space, instead preferring a tighter UI. Sort of like how Gmail lets you decide whether you want a lot of breathing room in your inbox or make it as small and tight as possible to see more content.
  4. Setting the primary color on the website. While this is entirely cosmetic, it is still charming to be able to set your favorite color on every button and link on a website.
  5. Enabling a high contrast mode. Someone pointed this out to me on GitHub. Apparently, many (and I mean many) CSS frameworks often fail the minimum contrast recommended between foreground and background colors on common elements, such as buttons. That list includes Halfmoon. This is often a tradeoff, because overly contrastive elements often look worse (purely in terms of aesthetic). User personalization can allow you to turn on a high contrast mode, if you have difficulty with the default contrast.

Allowing for user personalizations can be really difficult to pull off — especially for a framework — because that would could mean swapping out huge parts of CSS to accommodate the different personalization settings and combinations. However, with a framework like Halfmoon (i.e. built entirely using CSS variables), this becomes trivial as CSS variables can be set and changed on run-time using JavaScript, like so:

// Get the <html> tag (for reading and setting variables in global scope)
var myElement = document.documentElement;

// Read CSS variable
getComputedStyle(myElement).getPropertyValue("--variable-name");

// Set CSS variable
myElement.style.setProperty("--variable-name", "value");

Therefore, user personalization can be implemented using Halfmoon in the following way:

  • The user sets a preference. That basically means a variable value gets changed. The variable is set with JavaScript (as shown above), and the new value is stored in a cookie or local storage.
  • When the user comes back to the website, their preferences are retrieved and set using JavaScript (again, as shown above) once the page is loaded.

Here are visual examples to really hammer the point home.

Setting and saving the default font size

In the example above, whenever the range slider is changed, the variable --base-font-size is updated to the slider’s value. This is great for people who prefer larger text. As explained in the previous section, this new value can be saved in a cookie or local storage, and the next time the user visits the website, the user preference can be set on page load.

Setting the compactness of content

Compact theme using CSS variables
Because there are CSS custom properties used as utilities, like spacing and borders, we can remove or override them easily to create a more compact or expanded component layout.

Only two variables are updated in this example to go from an expanded view to a compact one:

  • --content-and-card-spacing changed from 3rem (30px) to 2rem (20px).
  • --card-border-radius changed from 0.4rem (4px) to 0.2rem (2px).

For a real life scenario, you could have a dropdown that asks the user whether they prefer their content to be Default or Compact, and choosing one would obviously set the above CSS variables to theme the site. Once again, this could be saved and set on page load when the user visits the website on their next session.

Wait, but why?

Even with all the examples I have shown so far, you may still be asking why is this actually necessary. The answer is really simple: one size does not fit all. In my estimate, around half of the population prefers a dark UI, while the other half prefers light. Similarly, people have wild variations about the things they like when it comes to design. User personalization is a form of improving the UX, because it lets the user choose what they prefer. This may not be so important on a landing page, but when it comes to a tool or dashboard (that one has to use for a long time to get something done), having a UI that can be personalized is a boon to productivity. And knowing that is what Halfmoon is designed to do makes it ideal for these types of use cases.

Moreover, you know how people often complain that websites made with a certain framework (eg Bootstrap) all look the same? This is a step toward making sure that websites built with Halfmoon will always look distinct, so that the focus is on the website and content itself, and not on the framework that was used to build it.

Again, I am not saying that everything should be allowed to be personalized. But knowing who the framework is for and what it is designed to do helps make it clear what should be personalized.

Looking ahead

I strongly feel that flexibility for customization and accounting for user preferences are often overlooked on the web, especially in the framework landscape. That’s what I’m trying to address with Halfmoon.

In the future, I want to make it a lot easier for developers to implement user preferences, and also promote diversity of design with new templates and themes. That said, here are some things on the horizon for Halfmoon:

  • A form validator (demo video)
  • New components, including range sliders, tabs and spinners
  • High contrast mode user preference
  • Multi-select component (like Select2, only without jQuery)
  • A date and time picker
  • A data-table component
  • A GUI-based form builder
  • More themes and templates

You can, of course, learn more about Halfmoon in the documentation website, and if you want to follow the project, you can give it a star on GitHub.



Source link

How might you go about designing a 3d-looking logo like this? I
Strategy

How might you go about designing a 3d-looking logo like this…


How might you go about designing a 3d-looking logo like this? I'm trying to use Illustrator but really struggling to get it to look right.

Would you do a flat line/isometric design or would you go down the extrude and bevel route? I'm stuck. Any tips would be awesome please. I can't get it looking right at all and I'm reaching the end of my tether!

https://preview.redd.it/cyn20y1npe161.png?width=484&format=png&auto=webp&s=7f4be18afe4047ba9e516a0b9795e12956a4c527

submitted by /u/Liebeniz
[comments]



Source link

I built a community platform from scratch in 7 days w/ Ruby on Rails, Tailwind, JawsDB, Heroku‍
Strategy

I built a community platform from scratch in 7 days w/ Ruby …


I built a community platform from scratch in 7 days w/ Ruby on Rails, Tailwind, JawsDB, Heroku‍

I have been running my own startup for 4+ yrs now, managing & doing most of the dev work. Earlier this year, I built a community platform from scratch in 7 days. I blogged about my experience & the process. Would love to hear your feedback on the community & the website.

Demo

https://reddit.com/link/k0rf7e/video/av6pvil5rd161/player

Full Blog Post: I built a community in 7 days

Community Website: Remote Clan

  • Custom built on top of Lobsters, computing-focussed community
  • Tech stack: Ruby on Rails, Tailwind, JawsDB, Heroku
  • Cost: $17/mo ($7 for Heroku dyno + $10 for JawsDB)
  • Time to launch: 7 days

If you prefer a quick read, you can check out this twitter thread instead.

submitted by /u/hrishikesh1990
[comments]





Source link

Issue with colours being viewed in Photoshop/Illustrator after being fine previously.
Strategy

Issue with colours being viewed in Photoshop/Illustrator aft…


Issue with colours being viewed in Photoshop/Illustrator after being fine previously.

Thought I would post in here as there us a huge community that may have had the same issue.

I opened both Photoshop & Illustrator today after around 2 weeks of inactivity to find an issue with the programs changing the look of files inside.

The files themselves when saved (after finding this issue) are still showing correctly when viewing them outside of PS/AI but inside the programs they are darker & more saturated. Here is a screenshot showing the issue with the photoshop image.

*Please note that before the issue started the Photoshop/Illustrator file when viewed in Photoshop/Illustrator looked identical to the image viewer file on the right in the attached image.

I have not manually changed any colour management settings between the times that I had no issues to now so I'm assuming an auto update has changed something.

This is extremely frustrating as now the programs themselves are

*Update*

Updating the Graphics driver manually did not fix the issue.

The colours when saved still save correctly. but it makes working on anything impossible due to the viewed colours being significantly darker & saturated than they actually are.

https://preview.redd.it/7fjj72jpzc161.png?width=1919&format=png&auto=webp&s=37832fb1affd8cc8fb6cc43ae849c6d588d41ce1

submitted by /u/wattyaknow
[comments]



Source link

r/graphic_design - Christmas wishes postcards ideas
Strategy

Christmas wishes postcards ideas : graphic_design


These are my alternatives for my personal Christmas postcards, which I’d be sending to my close folks. I want to ask for your honest opinions. The main motive of all alternatives is one of my personal most favourite buildings, which is iconic part of modern architecture for my city and right now is in danger.

The first two images are just slighty different variations of the same design. The two parts are front/back side of folding postcard (I know I’d have to flip the upper part upside down once I want to print it). On the upper part is an abstracted (less abstracted for the second variation) elevation of the building’s main facade (name and location of the building is mentioned in the lower left corner, as not all of my friends and family may recognise it). The diagonal lines represent shadows. The text above says Merry Christmas in my language (Slovak). On the lower half are portrayed some of the iconic sculptural work (lights, fountain) that can be seen near the building.

The third image is a completely different alternative, it is a collage made mostly from an old photograph of the same building. It is supposed to be have the image of a ‘lazy’ and rough graphic design.

As this is my first post here, I hope I am not breaking any rules. Hopefully you’ll like my work.

r/graphic_design - Christmas wishes postcards ideas
r/graphic_design - Christmas wishes postcards ideas
r/graphic_design - Christmas wishes postcards ideas



Source link

Delegates: Can’t Deal With Them, Can’t Live Without Them
Strategy

Delegates: Can’t Deal With Them, Can’t Live Without Them


Introduction

There is a famous quote by Jim Henson: “If you can’t beat themJoin them”.

It’s possible to write code for years without deliberately using delegates. I say “deliberately” because we may have used them without realizing it. Knowing what these types represent makes reading code easier. Knowing how to use them adds some useful tools to our developer toolbox. So instead of ignoring them, why not learn them. In this post, I will show you how you can get started with delegates and their use cases, which will encourage you to use them more in your code.

There are multiple definitions of delegates and one might make more sense over the other. Let’s see a few of those:

  • delegate is a type that represents a method with a specific signature and return type.
  • delegate is a pointer or a reference to a method.
  • delegate is a pipeline.

Still cryptic? Here are a few examples:

Examples:

A delegate representing a method that adds two numbers and returns a result:
delegate int AddNumbers(int value1, int value2);
A delegate representing a method that logs an exception and doesn't return anything:
delegate void LogException(Exception ex);
A delegate representing a function that converts some generic type to a string:
delegate string FormatAsString(T input);

Notice, The declaration of a delegate looks exactly like the declaration of a method, except with the keyword delegate in front of it.

Just like classes and interfaces, we can declare delegates outside of classes or nested within classes. We can mark them private, public, or internal.

With delegates, we can pass our methods (functions) around like values.

Delegates In Action

Let’s see a working example of delegates. Here I started with defining a delegate MathCalculation with accepts two floats and return a double. Then I created two variables of this type and assign methods to those variables. And finally, we invoke/call them and display the results:

So, in the delegate world, instead of calling the methods directly, we have now an indirection, we are invoking the methods using delegates instead. Having this capability offers a lot of flexibility which we will learn more about as we move forward. A common use case is to implement reactions to specific events and call-back methods.

Let’s see another example of delegates. The code is very similar to what we have already seen in the Calculator example:

Define a delegate:

Here is the ServerInfo class, we will use its method to assign to delegate types. Notice the methods signature match the delegate defined earlier: 

And here is code block which is using delegates:

output of this is shown below:

Events Relationship With Delegates

I’ve written few posts about .NET Events and you can check those if you need more information. Here I will just point out their relationship with delegates.

  • Events are associated with delegates.
  • A standard .NET Event accepts Sender and EventArgs.
  • The signature of EventHandler matches to delegate.
  • EventHandler<T> provides a simple way to create a custom delegate for an event.

Example Delegates

Let’s see another example of delegates:

I created classes to generate a static list of employees as shown below:

Here is the main method using these classes:

If we run the code it will display information as shown below:

Nothing special so far, the typical way of writing code.

Let’s imagine, that inside the loop, we don’t want to use Console.WriteLine directly or in other words, we dont want to hardcode the part which deals with printing. Instead, we want this logic to comes from somewhere else. This part can be sent as a parameter from outside. Sounds familiar? yes, we can use delegate which as we saw before is a pointer to a function.

I created a delegate called Print:

Then I defined the function which has the same signature as a delegate:

Now, we can use it in the main method as shown below:

And you might have already guessed it, running this code produces the same output, but we have now delegate indirection in-place: 

Now, update our code to wireup a different print delegate to display employee info:

I create a new method in PrintingUtils class which has the same signature as the delegate, just the implementation is different:

Here is the updated delegate wiring and usage code:

The output now shows the Id as well along with Name:

Notice that with the use of delegates our code is already beginning to be loosely coupled and you can take this ability to next level, implement business rules as delegates, and pass them around, etc. You can pass functions to other functions as well. There are a lot of places you can use delegates.

Summary

In this post, I tried to describe delegates and how to use those in your code. We also saw some information about their relationship with Events. You can read about Events in this post. The code for the demos is available on this link. Till next time, Happy Coding.



Source link

How to Load Fonts in a Way That Fights FOUT and Makes Lighth...
Strategy

How to Load Fonts in a Way That Fights FOUT and Makes Lighth…


A web font workflow is simple, right? Choose a few nice-looking web-ready fonts, get the HTML or CSS code snippet, plop it in the project, and check if they display properly. People do this with Google Fonts a zillion times a day, dropping its <link> tag into the <head>.

Let’s see what Lighthouse has to say about this workflow.

Stylesheets in the <head> have been flagged by Lighthouse as render-blocking resources and they add a one-second delay to render? Not great.

We’ve done everything by the book, documentation, and HTML standards, so why is Lighthouse telling us everything is wrong?

Let’s talk about eliminating font stylesheets as a render-blocking resource, and walk through an optimal setup that not only makes Lighthouse happy, but also overcomes the dreaded flash of unstyled text (FOUT) that usually comes with loading fonts. We’ll do all that with vanilla HTML, CSS, and JavaScript, so it can be applied to any tech stack. As a bonus, we’ll also look at a Gatsby implementation as well as a plugin that I’ve developed as a simple drop-in solution for it.

What we mean by “render-blocking” fonts

When the browser loads a website, it creates a render tree from the DOM, i.e. an object model for HTML, and CSSOM, i.e. a map of all CSS selectors. A render tree is a part of a critical render path that represents the steps that the browser goes through to render a page. For browser to render a page, it needs to load and parse the HTML document and every CSS file that is linked in that HTML.

Here’s a fairly typical font stylesheet pulled directly from Google Fonts:

@font-face {
  font-family: 'Merriweather';
  src: local('Merriweather'), url(https://fonts.gstatic.com/...) format('woff2');
}

You might be thinking that font stylesheets are tiny in terms of file size because they usually contain, at most, a few @font-face definitions. They shouldn’t have any noticeable effect on rendering, right?

Let’s say we’re loading a CSS font file from an external CDN. When our website loads, the browser needs to wait for that file to load from the CDN and be included in the render tree. Not only that, but it also needs to wait for the font file that is referenced as a URL value in the CSS @font-face definition to be requested and loaded.

Bottom line: The font file becomes a part of the critical render path and it increases the page render delay.

Critical render path delay when loading font stylesheet and font file 
(Credit: web.dev under Creative Commons Attribution 4.0 License)

What is the most vital part of any website to the average user? It’s the content, of course. That is why content needs to be displayed to the user as soon as possible in a website loading process. To achieve that, the critical render path needs to be reduced to critical resources (e.g. HTML and critical CSS), with everything else loaded after the page has been rendered, fonts included.

If a user is browsing an unoptimized website on a slow, unreliable connection, they will get annoyed sitting on a blank screen that’s waiting for font files and other critical resources to finish loading. The result? Unless that user is super patient, chances are they’ll just give up and close the window, thinking that the page is not loading at all.

However, if non-critical resources are deferred and the content is displayed as soon as possible, the user will be able to browse the website and ignore any missing presentational styles (like fonts) — that is, if they don’t get in the way of the content.

Optimized websites render content with critical CSS as soon as possible with non-critical resources deferred. A font switch occurs between 0.5s and 1.0s on the second timeline, indicating the time when presentational styles start rendering.

The optimal way to load fonts

There’s no point in reinventing the wheel here. Harry Roberts has already done a great job describing an optimal way to load web fonts. He goes into great detail with thorough research and data from Google Fonts, boiling it all down into a four-step process:

  • Preconnect to the font file origin.
  • Preload the font stylesheet asynchronously with low priority.
  • Asynchronously load the font stylesheet and font file after the content has been rendered with JavaScript.
  • Provide a fallback font for users with JavaScript turned off.

Let’s implement our font using Harry’s approach:

<!-- https://fonts.gstatic.com is the font file origin -->
<!-- It may not have the same origin as the CSS file (https://fonts.googleapis.com) -->
<link rel="preconnect"
      href="https://fonts.gstatic.com"
      crossorigin />


<!-- We use the full link to the CSS file in the rest of the tags -->
<link rel="preload"
      as="style"
      href="https://fonts.googleapis.com/css2?family=Merriweather&display=swap" />


<link rel="stylesheet"
      href="https://fonts.googleapis.com/css2?family=Merriweather&display=swap"
      media="print" onload="this.media='all'" />


<noscript>
  <link rel="stylesheet"
        href="https://fonts.googleapis.com/css2?family=Merriweather&display=swap" />
</noscript>

Notice the media="print" on the font stylesheet link. Browsers automatically give print stylesheets a low priority and exclude them as a part of the critical render path. After the print stylesheet has been loaded, an onload event is fired, the media is switched to a default all value, and the font is applied to all media types (screen, print, and speech).

Lighthouse is happy with this approach!

It’s important to note that self-hosting the fonts might also help fix render-blocking issues, but that is not always an option. Using a CDN, for example, might be unavoidable. In some cases, it’s beneficial to let a CDN do the heavy lifting when it comes to serving static resources.

Even though we’re now loading the font stylesheet and font files in the optimal non-render-blocking way, we’ve introduced a minor UX issue…

Flash of unstyled text (FOUT)

This is what we call FOUT:

Why does that happen? To eliminate a render-blocking resource, we have to load it after the page content has rendered (i.e. displayed on the screen). In the case of a low-priority font stylesheet that is loaded asynchronously after critical resources, the user can see the moment the font changes from the fallback font to the downloaded font. Not only that, the page layout might shift, resulting in some elements looking broken until the web font loads.

The best way to deal with FOUT is to make the transition between the fallback font and web font smooth. To achieve that we need to:

  • Choose a suitable fallback system font that matches the asynchronously loaded font as closely as possible.
  • Adjust the font styles (font-size, line-height, letter-spacing, etc.) of the fallback font to match the characteristics of the asynchronously loaded font, again, as closely as possible.
  • Clear the styles for the fallback font once the asynchronously loaded font file has has rendered, and apply the styles intended for the newly loaded font.

We can use Font Style Matcher to find optimal fallback system fonts and configure them for any given web font we plan to use. Once we have styles for both the fallback font and web font ready, we can move on to the next step.

Merriweather is the font and Georgia is the fallback system font in this example. Once the Merriweather styles are applied, there should be minimal layout shifting and the switch between fonts should be less noticeable.

We can use the CSS font loading API to detect when our web font has loaded. Why that? Typekit’s web font loader was once one of the more popular ways to do it and, while it’s tempting to continue using it or similar libraries, we need to consider the following:

  • It hasn’t been updated for over four years, meaning that if anything breaks on the plugin side or new features are required, it’s likely no one will implement and maintain them.
  • We are already handling async loading efficiently using Harry Roberts’ snippet and we don’t need to rely on JavaScript to load the font.

If you ask me, using a Typekit-like library is just too much JavaScript for a simple task like this. I want to avoid using any third-party libraries and dependencies, so let’s implement the solution ourselves and try to make it is as simple and straightforward as possible, without over-engineering it.

Although the CSS Font Loading API is considered experimental technology, it has roughly 95% browser support. But regardless, we should provide a fallback if the API changes or is deprecated in the future. The risk of losing a font isn’t worth the trouble.

The CSS Font Loading API can be used to load fonts dynamically and asynchronously. We’ve already decided not to rely on JavaScript for something simple as font loading and we’ve solved it in an optimal way using plain HTML with preload and preconnect. We will use a single function from the API that will help us check if the font is loaded and available.

document.fonts.check("12px 'Merriweather'");

The check() function returns true or false depending on whether the font specified in the function argument is available or not. The font size parameter value is not important for our use case and it can be set to any value. Still, we need to make sure that:

  • We have at least one HTML element on a page that contains at least one character with web font declaration applied to it. In the examples, we will use the &nbsp; but any character can do the job as long it’s hidden (without using display: none;) from both sighted and non-sighted users. The API tracks DOM elements that have font styles applied to them. If there are no matching elements on a page, then the API isn’t be able to determine if the font has loaded or not.
  • The specified font in the check() function argument is exactly what the font is called in the CSS.

I’ve implemented the font loading listener using CSS font loading API in the following demo. For example purposes, loading fonts and the listener for it are initiated by clicking the button to simulate a page load so you can see the change occur. On regular projects, this should happen soon after the website has loaded and rendered.

Isn’t that awesome? It took us less than 30 lines of JavaScript to implement a simple font loading listener, thanks to a well-supported function from the CSS Font Loading API. We’ve also handled two possible edge cases in the process:

  • Something goes wrong with the API, or some error occurs preventing the web font from loading.
  • The user is browsing the website with JavaScript turned off.

Now that we have a way to detect when the font file has finished loading, we need to add styles to our fallback font to match the web font and see how to handle FOUT more effectively.

The transition between the fallback font and web font looks smooth and we’ve managed to achieve a much less noticeable FOUT! On a complex site, this change would result in a fewer layout shifts, and elements that depend on the content size wouldn’t look broken or out of place.

What’s happening under the hood

Let’s take a closer look at the code from the previous example, starting with the HTML. We have the snippet in the <head> element, allowing us to load the font asynchronously with preload, preconnect, and fallback.

<body class="no-js">
  <!-- ... Website content ... -->
  <div aria-visibility="hidden" class="hidden" style="font-family: '[web-font-name]'">
      /* There is a non-breaking space here here */
  </div>
  <script> 
    document.getElementsByTagName("body")[0].classList.remove("no-js");
  </script>
</body>

Notice that we have a hardcoded .no-js class on the <body> element, which is removed the moment the HTML document has finished loading. This applies webfont styles for users with JavaScript disabled.

Secondly, remember how the CSS Font Loading API requires at least one HTML element with a single character to track the font and apply its styles? We added a <div> with a &nbsp; character that we are hiding from both sighted and non-sighted users in an accessible way, since we cannot use display: none;. This element has an inlined font-family: 'Merriweather' style. This allows us to smoothly switch between the fallback styles and loaded font styles, and make sure that all font files are properly tracked, regardless of whether they are used on the page or not.

Note that the &nbsp; character is not showing up in the code snippet but it is there!

The CSS is the most straightforward part. We can utilize the CSS classes that are hardcoded in the HTML or applied conditionally with JavaScript to handle various font loading states.

body:not(.wf-merriweather--loaded):not(.no-js) {
  font-family: [fallback-system-font];
  /* Fallback font styles */
}


.wf-merriweather--loaded,
.no-js {
  font-family: "[web-font-name]";
  /* Webfont styles */
}


/* Accessible hiding */
.hidden {
  position: absolute; 
  overflow: hidden; 
  clip: rect(0 0 0 0); 
  height: 1px;
  width: 1px; 
  margin: -1px;
  padding: 0;
  border: 0; 
}

JavaScript is where the magic happens. As described previously, we are checking if the font has been loaded by using the CSS Font Loading API’s check() function. Again, the font size parameter can be any value (in pixels); it’s the font family value that needs to match the name of the font that we’re loading.

var interval = null;


function fontLoadListener() {
  var hasLoaded = false;


  try {
    hasLoaded = document.fonts.check('12px "[web-font-name]"')
  } catch(error) {
    console.info("CSS font loading API error", error);
    fontLoadedSuccess();
    return;
  }
  
  if(hasLoaded) {
    fontLoadedSuccess();
  }
}


function fontLoadedSuccess() {
  if(interval) {
    clearInterval(interval);
  }
  /* Apply class names */
}


interval = setInterval(fontLoadListener, 500);

What’s happening here is we’re setting up our listener with fontLoadListener() that runs at regular intervals. This function should be as simple as possible so it runs efficiently within the interval. We are using the try-catch block to handle any errors and catch any issues so that web font styles still apply in the case of a JavaScript error so that the user doesn’t experience any UI issues.

Next, we’re accounting for when the font successfully loads with fontLoadedSuccess(). We need to make sure to first clear the interval so the check doesn’t unnecessarily run after it.  Here we can add class names that we need in order to apply the web font styles.

And, finally, we are initiating the interval. In this example, we’ve set it up to 500ms, so the function runs twice per second.

Here’s a Gatsby implementation

Gatsby does a few things that are different compared to vanilla web development (and even the regular create-react-app tech stack) which makes implementing what we’ve covered here a bit tricky.

To make this easy, we’ll develop a local Gatsby plugin, so all code that is relevant to our font loader is located at plugins/gatsby-font-loader in the example below.

Our font loader code and config will be split across the three main Gatsby files:

  • Plugin configuration (gatsby-config.js): We’ll include the local plugin in our project, list all local and external fonts and their properties (including the font name, and the CSS file URL), and include all preconnect URLs.
  • Server-side code (gatsby-ssr.js): We’ll use the config to generate and include preload and preconnect tags in the HTML <head> using setHeadComponents function from Gatsby’s API. Then, we’ll generate the HTML snippets that hide the font and include them in HTML using setPostBodyComponents.
  • Client-side code (gatsby-browser.js): Since this code runs after the page has loaded and after React starts up, it is already asynchronous. That means we can inject the font stylesheet links using react-helmet. We’ll also start a font loading listener to deal with FOUT.

You can check out the Gatsby implementation in the following CodeSandbox example.

I know, some of this stuff is complex. If you just want a simple drop-in solution for performant, asynchronous font loading and FOUT busting, I’ve developed a gatsby-omni-font-loader plugin just for that. It uses the code from this article and I am actively maintaining it. If you have any suggestions, bug reports, or code contributions, feel free to submit them on on GitHub.

Conclusion

Content is perhaps the most component to a user’s experience on a website. We need to make sure content gets top priority and loads as quickly as possible. That means using bare minimum presentation styles (i.e. inlined critical CSS) in the loading process. That is also why web fonts are considered non-critical in most cases — the user can still consume the content without them — so it’s perfectly fine for them to load after the page has rendered.

But that might lead to FOUT and layout shifts, so the font loading listener is needed to make a smooth switch between the fallback system font and the web font.

I’d like to hear your thoughts! Let me know in the comments how are you tackling the issue of web font loading, render-blocking resources and FOUT on your projects.


References



Source link

r/webdev - <form> doesn
Strategy

doesn’t display accents (é, à, û, ü) and “ç” correctly : we…


Greetings,

I’m having an issue with the HTML form. When I type a text using accents and special characters like this:

r/webdev - <form> doesn't display accents (é, à, û, ü) and "ç" correctly

After clicking on “Submit”, I see the following result:

userName=Américo Vespúcio
userPhone= 44700000000
userEmail=email@email.com
userMessage=Você está com o açúcar? [Portuguese]

Tu as déjà vu le travail d’un Développeur Web? Tu es sûr de ça? [French]

Kannst du die Türe schließen? [German]

This is my code:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Contact</title>
  </head>
  <body>
    <p>Contact form</p>

    <form action="mailto:myemail@email.com" method="POST" enctype="text/plain">
      <label>Name</label>
      <input type="text" name="userName"/><br/>
      <label>Phone</label>
      <input type="tel" name="userPhone"/><br/>
      <label>E-mail</label>
      <input type="email" name="userEmail"/><br/>
      <label>Your message:</label>
      <textarea name="userMessage" id="" cols="30" rows="10"></textarea><br/>
      <input type="submit"/>
    </form>
  </body>
</html>

Why am I having this problem if I’m using UTF-8 meta charset? And how can I solve this?



Source link