Chapter names in books, quotes from a speech, keywords in an article, stats on a report — these are all types of content that could be helpful to isolate and turn into a high-level summary of what’s important.
For example, have you seen the way Business Insider provides an article’s key points before getting into the content?
That’s the sort of thing we’re going to do, but try to extract the high points directly from the article using HTML Slot, HTML Template and Shadow DOM.
These three titular specifications are typically used as part of Web Components — fully functioning custom element modules meant to be reused in webpages.
Now, what we aim to do, i.e. text extraction, doesn’t need custom elements, but it can make use of those three technologies.
There is a more rudimentary approach to do this. For example, we could extract text and show the extracted text on a page with some basic script without utilizing slot and template. So why use them if we can go with something more familiar?
The reason is that using these technologies permits us a preset markup code (also optionally, style or script) for our extracted text in HTML. We’ll see that as we proceed with this article.
Now, as a very watered-down definition of the technologies we’ll be using, I’d say:
A template is a set of markup that can be reused in a page.
A slot is a placeholder spot for a designated element from the page.
A shadow DOM is a DOM tree that doesn’t really exist on the page till we add it using script.
We’ll see them in a little more depth once we get into coding. For now, what we’re going to make is an article that follows with a list of key points from the text. And, you probably guessed it, those key points are extracted from the article text and compiled into the key points section.
The key points are displayed as a list with a design in between the points. So, let’s first create a template for that list and designate a place for the list to go.
<article><!-- Article content --></article>
<!-- Section where the extracted keypoints will be displayed -->
<section id='keyPointsSection'>
<h2>Key Points:</h2>
<ul><!-- Extracted key points will go in here --></ul>
</section>
<!-- Template for the key points list -->
<template id='keyPointsTemplate'>
<li><slot name='keyPoints'></slot></li>
<li style="text-align: center;">⤙—⤚</li>
</template>
What we’ve got is a semantic
with a
where the list of key points will go. Then we have a for the list items that has two elements: one with a placeholder for the key points from the article and another with a centered design.
The layout is arbitrary. What’s important is placing a where the extracted key points will go. Whatever’s inside the will not be rendered on the page until we add it to the page using script.
Further, the markup inside can be styled using inline styles, or CSS enclosed by :
The fun part! Let’s pick the key points from the article. Notice the value of the name attribute for the inside the (keyPoints) because we’ll need that.
<article>
<h1>Bears</h1>
<p>Bears are carnivoran mammals of the family Ursidae. <span><span slot='keyPoints'>They are classified as caniforms, or doglike carnivorans</span></span>. Although only eight species of bears <!-- more content --> and partially in the Southern Hemisphere. <span><span slot='keyPoints'>Bears are found on the continents of North America, South America, Europe, and Asia</span></span>.<!-- more content --></p>
<p>While the polar bear is mostly carnivorous, <!-- more content -->. Bears use shelters, such as caves and logs, as their dens; <span><span slot='keyPoints'>Most species occupy their dens during the winter for a long period of hibernation</span></span>, up to 100 days.</p>
<!-- More paragraphs -->
</article>
The key points are wrapped in a carrying a slot attribute value (“keyPoints“) matching the name of the placeholder inside the .
Notice, too, that I’ve added another outer wrapping the key points.
The reason is that slot names are usually unique and are not repeated, because one matches one element using one slot name. If there’re more than one element with the same slot name, the placeholder will be replaced by all those elements consecutively, ending in the last element being the final content at the placeholder.
So, if we matched that one single inside the against all of the elements with the same slot attribute value (our key points) in a paragraph or the whole article, we’d end up with only the last key point present in the paragraph or the article in place of the .
That’s not what we need. We need to show all the key points. So, we’re wrapping the key points with an outer to match each of those individual key points separately with the . This is much more obvious by looking at the script, so let’s do that.
const keyPointsTemplate = document.querySelector('#keyPointsTemplate').content;
const keyPointsSection = document.querySelector('#keyPointsSection > ul');
/* Loop through elements with 'slot' attribute */
document.querySelectorAll('[slot]').forEach((slot)=>{
let span = slot.parentNode.cloneNode(true);
span.attachShadow({ mode: 'closed' }).appendChild(keyPointsTemplate.cloneNode(true));
keyPointsSection.appendChild(span);
});
First, we loop through every with a slot attribute and get a copy of its parent (the outer ). Note that we could also loop through the outer directly if we’d like, by giving them a common class value.
This “attachment” causes the inside the template’s list item in the shadow tree to absorb the inner carrying its matching slot name, i.e. our key point.
The slotted key point is then added to the key points section at the end of the page (keyPointsSection.appendChild(span)).
This happens with all the key points in the course of the loop.
That’s really about it. We’ve snagged all of the key points in the article, made copies of them, then dropped the copies into the list template so that all of the key points are grouped together providing a nice little CliffsNotes-like summary of the article.
What do you think of this technique? Is it something that would be useful in long-form content, like blog posts, news articles, or even Wikipedia entries? What other use cases can you think of?
I’ve definitely been guilty of thinking about rendering on the web as a two-horse race. There is Server-Side Rendering (SSR, like this WordPress site is doing) and Client-Side Rendering (CSR, like a typical React app). Both are full of advantages and disadvantages. But, of course, the conversation is more nuanced. Just because an app is SSR doesn’t mean it doesn’t do dynamic JavaScript-powered things. And just because an app is CSR doesn’t mean it can’t leverage any SSR at all.
It’s a spectrum! Jason Miller and Addy Osmani paint that picture nicely in Rendering on the Web.
My favorite part of the article is the infographic table they post at the end of it. Unfortunately, it’s a PNG. So I took a few minutes and
We’ve been writing a lot about refactoring CSS lately, from how to take a slow and methodical approach to getting some quick wins. As a result, I’ve been reading a ton about this topic and somehow stumbled upon this post by Harry Roberts about refactoring and how to mitigate the potential risks that come with it:
Refactoring can be scary. On a sufficiently large or legacy application, there can be so much fundamentally wrong with the codebase that many refactoring tasks will run very deep throughout the whole project. This puts a lot of pressure on developers, especially considering that this is their chance to “get it right this time”. This can feel debilitating: “Where do I start?” “How long is this going to take?” “How will I know if I’m doing the right thing?”
Harry then comes up with this metaphor of a refactoring tunnel where it’s really easy to find yourself stuck in the middle of a refactor and without any way out of it. He argues that we should focus on small, manageable pieces instead of trying to tackle everything at once:
Resist the temptation to refactor anything that runs right the way throughout the project. Instead, identify smaller and more manageable tasks: tasks that have a much smaller surface area, and therefore a much shorter Refactoring Tunnel.
These tasks can still aim toward a larger and more total goal but can be realised in much safer and shorter timeframes. Want to move all of your classes from BEM to BEM(IT)? Sure, but maybe just implement it on the nav first.
This way feels considerably slower, for sure, but there’s so much less risk involved.
Today, I’d like to invite you to build an endless runner VR game with webVR — a framework that gives a dual advantage: It can be played with or without a VR headset. I’ll explain the magic behind the gaze-based controls for our VR-headset players by removing the game control’s dependence on a keyboard.
In this tutorial, I’ll also show you how you can synchronize the game state between two devices which will move you one step closer to building a multiplayer game. I’ll specifically introduce more A-Frame VR concepts such as stylized low-poly entities, lights, and animation.
A virtual reality headset (optional, recommended). (I use Google Cardboard, which is offered at $15 a piece.)
Note: A demo of the final product can be viewed here.
Step 1: Setting Up A Basic Scene
In this step, we will set up the following scene for our game. It is composed of a few basic geometric shapes and includes custom lighting, which we will describe in more detail below. As you progress in the tutorial, you will add various animations and effects to transform these basic geometric entities into icebergs sitting in an ocean.
You will start by setting up a website with a single static HTML page. This allows you to code from your desktop and automatically deploy to the web. The deployed website can then be loaded on your mobile phone and placed inside a VR headset. Alternatively, the deployed website can be loaded by a standalone VR headset.
Get started by navigating to glitch.com. Then, do the following:
Click on “New Project” in the top right.
Click on “hello-webpage” in the drop down.
Next, click on index.html in the left sidebar. We will refer to this as your “editor”.
Start by deleting all existing code in the current index.html file. Then, type in the following for a basic webVR project, using A-Frame VR. This creates an empty scene by using A-Frame’s default lighting and camera.
<!DOCTYPE html>
<html>
<head>
<title>Ergo | Endless Runner Game in Virtual Reality</title>
<script src="https://aframe.io/releases/0.7.0/aframe.min.js"></script>
</head>
<body>
<a-scene>
</a-scene>
</body>
</html>
Note: You can learn more about A-Frame VR at aframe.io.
To start, add a fog, which will obscure objects far away for us. Modify the a-scene tag on line 8.
Moving forward, all objects in the scene will be added between the ... tags. The first item is the sky. Between your a-scene tags, add the a-sky entity.
After your sky, add lighting to replace the default A-Frame lighting.
There are three types of lighting:
Ambient
This is an ever-present light that appears to emanate from all objects in the scene. If you wanted a blue tint on all objects, resulting in blue-ish shadows, you would add a blue ambient light. For example, the objects in this Low Poly Island scene are all white. However, a blue ambient light results in a blue hue.
Directional
This is analogous to a flashlight which, as the name suggests, points in a certain direction.
Point
Again, as the name suggests, this emanates light from a point.
Just below your a-sky entity, add the following lights: one directional and one ambient. Both are light blue.
Next, add a platform for our endless runner game to take place on. We will represent this platform using the side of a large cone. After the box above, add the following:
To preview the webpage, click on “Preview” in the top left. We will refer to this as your preview. Note that any changes in your editor will be automatically reflected in this preview, barring bugs or unsupported browsers.
In your preview, you will see the following basic virtual reality scene. You can view this scene by using your favorite VR headset.
This concludes the first step, setting up the game scene’s basic geometric objects. In the next step, you will add animations and use other A-Frame VR libraries for more visual effects.
Step 2: Improve Aesthetics for Virtual Reality Scene
In this step, you will add a number of aesthetic improvements to the scene:
Low-poly objects
You will substitute some of the basic geometric objects with their low-poly equivalents for more convincing, irregular geometric shapes.
Animations
You will have the player bob up and down, move the icebergs slightly, and make the ocean a moving body of water.
Your final product for this step will match the following:
To start, import A-Frame low-poly components. In ..., add the following JavaScript import:
The A-Frame low-poly library implements a number primitives, such as lp-cone and lp-sphere, each of which is a low-poly version of an A-Frame primitive. You can learn more about A-Frame primitives over here.
Next, navigate to the section of your code. Replace all s with .
We will now configure the low-poly primitives. All low-poly primitive supports two attributes, which control how exaggerated the low-poly stylization is:
amplitude
This is the degree of stylization. The greater this number, the more a low-poly shape can deviate from its original geometry.
amplitude-variance
This is how much stylization can vary, from vertex to vertex. The greater this number, the more variety there is in how much each vertex may deviate from its original geometry.
To get a better intuition for what these two variables mean, you can modify these two attributes in the A-Frame low-poly demo.
For the first iceberg, set amplitude-variance to 0.25. For the second iceberg, set amplitude to 0.12. For the last iceberg, set amplitude to 0.1.
Navigate to your preview, and you should see the low-poly icebergs bobbing around.
Next, update the platform and associated player. Here, upgrade the cone to a low-poly object, changing a-cone to lp-cone for . Additionally, add configurations for amplitude.
Navigate to your preview, and you will see your player bobbing up and down, with a fluctuating light on a low-poly platform.
Next, let’s animate the ocean. Here, you can use a lightly-modified version of Don McCurdy’s ocean. The modifications allow us to configure how large and fast the ocean’s waves move.
Create a new file via the Glitch interface, by clicking on “+ New File” on the left. Name this new file assets/ocean.js. Paste the following into your new ocean.js file:
/**
* Flat-shaded ocean primitive.
* https://github.com/donmccurdy/aframe-extras
*
* Based on a Codrops tutorial:
* http://tympanus.net/codrops/2016/04/26/the-aviator-animating-basic-3d-scene-threejs/
*/
AFRAME.registerPrimitive('a-ocean', {
defaultComponents: {
ocean: {},
rotation: {x: -90, y: 0, z: 0}
},
mappings: {
width: 'ocean.width',
depth: 'ocean.depth',
density: 'ocean.density',
amplitude: 'ocean.amplitude',
'amplitude-variance': 'ocean.amplitudeVariance',
speed: 'ocean.speed',
'speed-variance': 'ocean.speedVariance',
color: 'ocean.color',
opacity: 'ocean.opacity'
}
});
AFRAME.registerComponent('ocean', {
schema: {
// Dimensions of the ocean area.
width: {default: 10, min: 0},
depth: {default: 10, min: 0},
// Density of waves.
density: {default: 10},
// Wave amplitude and variance.
amplitude: {default: 0.1},
amplitudeVariance: {default: 0.3},
// Wave speed and variance.
speed: {default: 1},
speedVariance: {default: 2},
// Material.
color: {default: '#7AD2F7', type: 'color'},
opacity: {default: 0.8}
},
/**
* Use play() instead of init(), because component mappings – unavailable as dependencies – are
* not guaranteed to have parsed when this component is initialized.
* /
play: function () {
const el = this.el,
data = this.data;
let material = el.components.material;
const geometry = new THREE.PlaneGeometry(data.width, data.depth, data.density, data.density);
geometry.mergeVertices();
this.waves = [];
for (let v, i = 0, l = geometry.vertices.length; i
Navigate back to your index.html file. In the of your code, import the new JavaScript file:
Ensure that your index.html code matches the Step 2 source code. Navigate to your preview, and you’ll find the updated ocean along with a white circle fixed to the center of your view.
This concludes your aesthetic improvements to the scene. In this section, you learned how to use and configure low-poly versions of A-Frame primitives, e.g. lp-cone. In addition, you added a number of animations for different object attributes, such as position, rotation, and light intensity. In the next step, you will add the ability for the user to control the player — just by looking at different lanes.
Step 3: Add Virtual Reality Gaze Controls
Recall that our audience is a user wearing a virtual reality headset. As a result, your game cannot depend on keyboard input for controls. To make this game accessible, our VR controls will rely only on the user’s head rotation. Simply look to the right to move the player to the right, look to the center to move to the middle, and look to the left to move to the left. Our final product will look like the following.
Note: The demo GIF below was recorded on a desktop, with user drag as a substitute for head rotation.
Start from your index.html file. In the ... tag, import your new JavaScript file, assets/ergo.js. This new JavaScript file will contain the game’s logic.
Then, add a new lane-controls attribute to your a-camera object:
<!-- Camera -->
<a-camera lane-controls position...>
</a-camera>
Next, create your new JavaScript file using “+ New File” to the left. Use assets/ergo.js for the filename. For the remainder of this step, you will be working in this new JavaScript file. In this new file, define a new function to setup controls, and invoke it immediately. Make sure to include the comments below, as we will refer to sections of code by those names.
/************
* CONTROLS *
************/
function setupControls() {
}
/********
* GAME *
********/
setupControls();
Note: The setupControls function is invoked in the global scope, because A-Frame components must be registered before thetag. I will explain what a component is below.
In your setupControls function, register a new A-Frame component. A component modifies an entity in A-Frame, allowing you to add custom animations, change how an entity initializes, or respond to user input. There are many other use cases, but you will focus on the last one: responding to user input. Specifically, you will read user rotation and move the player accordingly.
In the setupControls function, register the A-Frame component we added to the camera earlier, lane-controls. We will add an event listener for the tick event. This event triggers at every animation frame. In this event listener, hlog output at every tick.
Navigate to your preview. Open your browser developer console by right-clicking anywhere and selecting “Inspect”. This applies to Firefox, Chrome, and Safari. Then, select “Console” from the top navigation bar. Ensure that you see timestamps flowing into the console.
Navigate back to your editor. Still in assets/ergo.js, replace the body of setupControls with the following. Fetch the camera rotation using this.el.object3D.rotation, and log the lane to move the player to.
function setupControls() {
AFRAME.registerComponent('lane-controls', {
tick: function (time, timeDelta) {
var rotation = this.el.object3D.rotation;
if (rotation.y > 0.1) console.log("left");
else if (rotation.y
Navigate back to your preview. Again, open your developer console. Try rotating the camera slightly, and observe console output update accordingly.
Before the controls section, add three constants representing the left, middle, and right lane x values.
At the start of the controls section, define a new global variable representing the player position.
/************
* CONTROLS *
************/
// Position is one of 0 (left), 1 (center), or 2 (right)
var player_position_index = 1;
function setupControls() {
...
After the new global variable, define a new function that will move the player to each lane.
var player_position_index = 1;
/**
* Move player to provided index
* @param {int} Lane to move player to
*/
function movePlayerTo(position_index) {
}
function setupControls() {
...
Inside this new function, start by updating the global variable. Then, define a dummy position.
function movePlayerTo(position_index) {
player_position_index = position_index;
var position = {x: 0, y: 0, z: 0}
}
After defining the position, update it according to the function input.
function movePlayerTo(position_index) {
...
if (position_index == 0) position.x = POSITION_X_LEFT;
else if (position_index == 1) position.x = POSITION_X_CENTER;
else position.x = POSITION_X_RIGHT;
}
Finally, update the player position.
function movePlayerTo(position_index) {
...
document.getElementById('player').setAttribute('position', position);
}
Double-check that your function matches the following.
/**
* Move player to provided index
* @param {int} Lane to move player to
*/
function movePlayerTo(position_index) {
player_position_index = position_index;
var position = {x: 0, y: 0, z: 0}
if (position_index == 0) position.x = POSITION_X_LEFT;
else if (position_index == 1) position.x = POSITION_X_CENTER;
else position.x = POSITION_X_RIGHT;
document.getElementById('player').setAttribute('position', position);
}
Navigate back to your preview. Open the developer console. Invoke your new movePlayerTo function from the console to ensure that it functions.
> movePlayerTo(2) # should move to right
Navigate back to your editor. For the final step, update your setupControls to move the player depending on camera rotation. Here, we replace the console.log with movePlayerTo invocations.
function setupControls() {
AFRAME.registerComponent('lane-controls', {
tick: function (time, timeDelta) {
var rotation = this.el.object3D.rotation;
if (rotation.y > 0.1) movePlayerTo(0);
else if (rotation.y
Ensure that your assets/ergo.js matches the corresponding file in the Step 3 source code. Navigate back to your preview. Rotate the camera from side to side, and your player will now track the user’s rotation.
This concludes gaze controls for your virtual reality endless runner game.
In this section, we learned how to use A-Frame components and saw how to modify A-Frame entity properties. This also concludes part 1 of our endless runner game tutorial. You now have a virtual reality model equipped with aesthetic improvements like low-poly stylization and animations, in addition to a virtual-reality-headset-friendly gaze control for players to use.
Conclusion
We created a simple, interactive virtual reality model, as a start for our VR endless runner game. We covered a number of A-Frame concepts such as primitives, animations, and components — all of which are necessary for building a game on top of A-Frame VR.
Here are extra resources and next steps for working more with these technologies:
A-Frame VR
Official documentation for A-Frame VR, covering the topics used above in more detail.
A-Frame Homepage
Examples of A-Frame projects, exhibiting different A-Frame capabilities.
Low-Poly Island
VR model using the same lighting, textures, and animations as the ones used for this endless runner game.
In the next part of this article series, I’ll show you how you can implement the game’s core logic and use more advanced A-Frame VR scene manipulations in JavaScript.
Ten years ago the network was the main bottleneck. Today, the main bottleneck is JavaScript. The amount of JavaScript on pages is growing rapidly (nearly 5x in the last 7 years). In order to keep pages rendering and feeling fast, we need to focus on JavaScript CPU time to reduce blocking the browser main thread.
… blocks large scripts, sets budgets for certain resource types (script, font, css, images), turns off document.write(), clobbers sync XHR, enables client-hints pervasively, and buffers resources without Content-Length set.
Craig Hockenberry, posting an idea to the WebKit bug tracker:
Without limits, there is no incentive for a JavaScript developer to keep their codebase small and dependencies minimal. It’s easy to add another framework, and that framework adds another framework, and the next thing you know you’re loading tens of megabytes of data just to display a couple hundred kilobytes of content. …
The situation I’m envisioning is that a site can show me any advertising they want as long as they keep the overall size under a fixed amount, say one megabyte per page. If they work hard to make their site efficient, I’m happy to provide my eyeballs.
It’s easy to point a finger at frameworks and third-party scripts for large amounts of JavaScript. If you’re interested in hearing more about the size of frameworks, you might enjoy me and Dave discussing it with Jason Miller.
And speaking of third-parties, Patrick Hulce created Third Party Web: “This document is a summary of which third-party scripts are most responsible for excessive JavaScript execution on the web today.”
Sometimes name-and-shame is an effective tactic to spark change.
Addy Osmani writes about an ESLint rule that prohibits particular packages, of which you could use to prevent usage of known-to-be-huge packages. So if someone tries to load the entirety of lodash or moment.js, it can be stopped at the linting level.
Tim Kadlec ties the threads together very well in “Limiting JavaScript?” If your gut reaction on this is that JavaScript is being unfairly targeted as a villain, Tim acknowledges that:
One common worry I saw voiced was “if JavaScript, why not other resources too?”. It’s true; JavaScript does get picked on a lot though it’s not without reason. Byte for byte, JavaScript is the most significant detriment to performance on the web, so it does make sense to put some focus on reducing the amount we use.
However, the point is valid. JavaScript may be the biggest culprit more often than not, but it’s not the only one.
I’m never going to tell you that writing your CSS in CSS (or some syntactic preprocessor) is a bad idea. I think you can be perfectly productive and performant without any tooling at all. But, I also think writing CSS in JavaScript is a good idea for component-based styles in codebases that build all their components with JavaScript anyway.
In this article, Max Stoiber focuses on why to write CSS in JavaScript rather than how to do it. There is one reason that resonates strongly with me, and that’s confidence. This is what styling confidence means to me.
Anyone on a team can work on styling a component without any fear of unintended side effects.
There is no pressure to come up with perfect names that will work now and forever.
There is no worry about the styles needing to be extremely re-usable or that they play friendly with anything else. These styles will only be used when needed and not any other time.
There is an obvious standard to where styles are placed in the codebase.
CSS in JavaScript isn’t the only answer to those things, but as Max connects to other posts on the topic, it can lead to situations where good choices happen naturally.
There are some reasons why I don’t buy into it. Performance is one of them, like choosing CSS-in-JS is some automatic performance win. Part of the problem (and I’m guilty of doing it right here) is that CSS-in-JS is a wide scope of solutions. I’ve generally found there is no big performance wins in CSS-in-JS (more likely the opposite), but that’s irrelevant if we’re talking about something like CSS modules with the styles extracted and linked up like any other CSS.
Container units are a specialized set of CSS variables that allow you to build grids, layouts, and components using columns and gutters. They mirror the layout functionality found in UI design software where configuring just three values provides your document with a global set of columns and gutters to measure and calculate from.
They also provide consistent widths everywhere in your document — regardless of their nesting depth, their parent’s width, or their sibling elements. So instead of requiring a repeated set of .grid and .row parent elements, container units measure from the :root of your document — just like using a rem unit.
What Makes Container Units Different?
Grids from popular frameworks (such as Bootstrap or Bulma) share the same fundamental limitation: they rely on relative units such as ‘percentages’ to build columns and gutters.
This approach ties developers to using a specific HTML structure whenever they want to use those measurements and requires parent > child nesting for widths to calculate correctly.
Using DevTools, drag that element somewhere else in the document;
Note that the column’s width has changed in transit.
Freedom Of Movement (…Not Brexit)
Container units allow you more freedom to size elements using a set of global units. If you want to build a sidebar the width of three columns, all you need is the following:
Your ...class="sidebar">... element can live anywhere inside of your document — without specific parent elements or nesting.
Sharing Tools With Designers
Designers and developers have an excellent middle-ground that helps translate from design software to frontend templates: numbers.
Modular scales are exceptional not just because they help designers bring harmony to their typography, but also because developers can replicate them as a simple system. The same goes for Baseline Grids: superb, self-documenting systems with tiny configuration (one root number) and massive consistency.
Container units are set up in the same way that designers use Sketch to configure Layout Settings:
Any opportunity for designers and developers to build with the same tools is a huge efficiency boost and fosters new thinking in both specialisms.
These three values define how wide a column is in proportion to your grid. In the example above, a column’s width is 60 / 960. Gutters are calculated automatically from the remaining space.
Finally, set a width for your container:
:root {
--container-width: 84vw;
}
Note: --container-widthshould be set as an absolute unit. I recommend usingviewport unitsorrems.
You can update your --container-width at any breakpoint (all of your container units will update accordingly):
Of course, you can use pre-processor shortcuts for every container unit I’ve mentioned so far. Let’s put them to the test with a design example.
Building Components With Container Units
Let’s take a design example and break it down:
This example uses columns, gutters and column spans. Since we’re just storing a value, container units can be used for other CSS properties, like defining a height or providing padding:
“Pre-processors have been able to do that for years with $variables — why do you need CSS variables?”
Not… quite. Although you can use variables to run calculations, you cannot avoid compiling unnecessary code when one of the variables updates it’s value.
Let’s take the following condensed example of a grid:
This example shows how every reference to a SASS/LESS variable has to be re-compiled if the variable changes — duplicating code over and over for each instance.
But CSS Variables share their logic with the browser, so browsers can do the updating for you.
This concept helps form the logic of container units; by storing logic once at the root, every element in your document watches those values as they update, and responds accordingly.
Social media is an interesting thing. As web design professionals, it can be a great opportunity to share your work and get in front of prospects who might otherwise never find you online. But for as much good as social media can do to propel your marketing efforts, it has the potential to be just as harmful to your business.
Social Media Mistakes That Can Harm Your Web Design Business
CareerBuilder surveyed over 1,000 people involved in the hiring process for organizations last year. What they found about the relationship between hiring and social media is quite startling.
Even if you’re not applying to work full-time for someone else’s company, hiring managers are likely to go through a similar process when evaluating you for a freelance position. So, pay close attention to these statistics:
70% of hiring organizations research candidates using social media;
57% of hiring managers have chosen not to pursue a candidate because of what they found on social media;
47% of organizations won’t contact a candidate if they have no online presence.
Basically, companies are looking for a reason NOT to work with you. So, you must tread very carefully when using social media marketing.
Here are some things to avoid:
1. No Online Presence
It’s funny. Use social media the wrong way, and prospective clients will rule you out as their next possible freelance designer. But don’t use social media at all, and the same can happen.
There’s a delicate balance you must strike.
In order to stop losing work because you don’t have an online presence, avoid the following:
Staying away from social media at all costs;
Creating profiles on social, but never posting anything;
Creating profiles on social, but leaving your profile incomplete, inaccurate or outdated.
Too much time spent on social media can hurt you as well — especially if you’re taking part in irrelevant conversations. If you’re pointing potential clients to your branded social media pages, then you better be engaging in conversations that fit with your niche.
Remember: clients are looking at social media to get a sense for your professionalism and expertise. They aren’t trying to hire someone who hops in and out of social media 20 times a day. (How would you get your work done in that case?) So, keep your activity to a reasonable amount.
Social media is a free way to get in front of prospects and to learn more about what’s going on in web design! Unlike blogging and email marketing, you don’t have to be a good writer to effectively connect with others either. Just write a few words and let your images (and links) shine.
That said, you don’t want to simply copy-and-paste the headline of an article into your social media post and call it a day. If you expect people to click on the links you’re sharing or engage with your posts, you have to say something worth listening to. If your social feed just ends up with a bunch of blog post titles and links, it’ll either look like you don’t care about sharing valuable content or you don’t understand your space very well.
As a general rule, you should share 20% of your own stuff on social media and 80% of stuff from other people. This way, it doesn’t seem as though you’re only there to promote your business and make more money. The reason people are willing to engage with freelancers and businesses on social media is to make a connection; not to have another sales pitch thrown in their faces.
That said, you want to be very careful about the links, memes, and other content you share. When sharing content, it should be:
Published within the last week so it’s still timely and relevant;
Positive;
Valuable and not just a keyword-stuffed piece to boost SEO;
In line with your own web design practices;
Factually accurate.
Even if you didn’t write the content, it can still reflect poorly on you for sharing something of bad quality.
It’s easy to get wrapped up in super-charged conversations that take place on social media, especially if people are talking about matters that are close to your heart. But that’s not what future clients want to see. They want to know that you’re a professional, that you’ll come prepared to work and not bring your personal life into it. Getting involved in the wrong conversations will tell them otherwise.
These are just some of the risky topics you’ll want to avoid:
This one is a huge no-no. It doesn’t matter how much of the web design community will rally behind you when a client fails to pay or you’re fed up with their endless rounds of feedback. Social media is absolutely not the place to badmouth a client — even if you don’t name them.
The same goes for leaking any confidential or sensitive information you agreed to keep private. (Check your freelance contract. I guarantee it’s in there.) Just don’t do it.
If you need to vent, do so with your designer pals in private. Prospective clients will see your ease with badmouthing an old or current client and take that as a sign that you’re someone not to be trusted.
As a web designer, you have to be on social media for the purposes of marketing. Just remember that what you put out there will be judged just as harshly as the content you put on your own website. And, really, that should be a basic rule of thumb for social media:
Never put something out there that you wouldn’t be willing to publish on your website.
Every online business you can name has one goal: increase sales and profit as much as possible. In the online environment, this all starts with a simple process: increasing traffic on your website. Look at it from this perspective, if you have an online site but you do not have enough traffic, it consumes your resources in vain.
And yet, there are so many online sites that don’t benefit from user visits. Why? To be honest, there are quite a few reasons this could be. Below are the top 10 reasons.
1. SEO optimization on irrelevant words
Each niche has certain keywords that need they aim for to rank decently in Google. If you sell TVs and you rank on Google for audio systems, it doesn’t really help you. In addition, if you try to rank yourself for an extremely general keyword (like the word “TVs”) you will notice that it’s pretty much impossible to rank decently, or, at the very least, there’s an insane amount of competition.
You have to choose something specific. Keyword research is a vital part of SEO for this very reason. Identify what users are looking for on Google and optimize those words.
2. Faulty links
Another very common cause that affects traffic is lost or defective links on the site. Have you ever gotten that infamous error 404? In this case, check where that page is on your site. You may have redirected the page, or you may have changed the URL and did not make the interconnection by 301 redirects. This can lead to the loss of links, and this will surely lead to a decrease in traffic.
3. Penalties from Google
Google’s penalties are quite rare, but cannot be eliminated from the equation. If you’ve tried to optimize the website, if you have multiple suspicious links to your site, if you have duplicate content, or if you do not respect Google’s rules, it will penalize you. If your website traffic has fallen sharply, or if you simply launch the site and fail to get visits, that’s probably the cause.
4. Site loading speed
Ask anyone, absolutely anyone, and they’ll tell you that slow loading screens will make them click away. People use the internet for their convenience, if you interrupt that in any way, you’ve most likely lost traffic.
Every website should be optimized for the fastest loading speed possible. Here are a few, very quick ways to do that:
Use images sparingly
Limit the amount of GIFs and videos
Condense text
Simply explained, if your website has a lot to load, it will take a while. You have to find the balance between necessary and extra content.
5. Server issues – low-quality hosting
And here, the problems are quite common. Visits to your site can be clearly influenced by the quality of your chosen hosting. Perhaps the site is offline fixed when a large part of your users enters it. To have quality hosting, make sure that uptime is guaranteed, as high as possible. What is the uptime of a website? You can see this here.
6. Navigation issues
The way users are able to navigate your site can influence traffic. If the user finds it hard to find the information they want on the site, they will definitely leave. Try to make a site as clear as possible, with an airy and clean structure. And do not forget, the content is also extremely important. It must walk the user up to the final step – the command. If he does not find interesting content on the site, the user will leave.
The answer here, for most sites, is almost always placing the search bar in an easy to find area. Don’t hide it, make the icon small, or neglect to use one entirely. Place that search bar front and center.
7. Unoptimized mobile site
Over 70% of current traffic comes from mobile, and we can expect those numbers to go up increasingly in the next few years. A website that is not optimized for mobile will have clear deficiencies in attracting traffic. From the start, your website should be optimized for mobile. As more and more users are starting to use mobile devices to find what they’re looking for, your website can become obsolete really fast if it’s not mobile optimized.
8. Lack of promotion
You can’t find a website if you don’t know about its existence. It’s not enough to build a model website if it’s not promoted as it should be. Online promotion is the best way to bring traffic to a site, especially if it’s new. Closely related to SEO optimization, promotion is an unimaginable growth engine for your business.
With that said, look for ways to advertise your website. Whether it’s organic growth, or growth created by paid ads, growth is still growth.
9. Poor or unattractive content
If your site does not tell a story to the visitor, they will leave and avoid ever landing on your site ever again. Poor, unskilled, and unattractive content for the user will quickly deter users from visiting your website. Try to maximize the attractiveness of content through copywriting techniques in terms of text, by combining text with pictures and techniques for creating interactive content.
10. Incorrect indexing
Indexing is the process by which Google sends crawlers to your site to view, categorize content, and position you on the search engine. If the indexing was done incorrectly (either because of technical difficulties or because of mistakes made by you in optimizing the site), it will not appear on Google and therefore will not have traffic. Make sure your site is properly optimized and reindeer it on Google.
The conclusion
Hopefully you don’t have any of these issues, but if you do, try to get them resolved as quickly as possible. The web holds infinite possibilities for us all, and it’s worth taking advantage of them.
Even if you are happy with your current site traffic, try approaching a few of these issues and optimize your website as best as you can. In the end, hopefully you’ll see growth, and we all want that, don’t we?
I like Adam Laki’s Quick Tip: CSS Triangles because it covers that ubiquitous fact about front-end techniques: there are always many ways to do the same thing. In this case, drawing a triangle can be done:
with border and a collapsed element
with clip-path: polygon()
with transform: rotate() and overflow: hidden
with glyphs like ?
I’d say that the way I’ve typically done triangles the most over the years is with the border trick, but I think my favorite way now is using clip-path. Code like this is fairly clear, understandable, and maintainable to me: clip-path: polygon(50% 0, 0 100%, 100% 100%); Brain: Middle top! Bottom right! Bottom left! Triangle!
My 2nd Place method goes to an option that didn’t make Adam’s list: inline ! This kind of thing is nearly just as brain-friendly: .