Beating The End Of The Year Rush (December 2019 Wallpapers Edition)

Beating The End Of The Year Rush (December 2019 Wallpapers Edition)

Beating The End Of The Year Rush (December 2019 Wallpapers Edition)

Cosima Mielke

As the year is coming to a close, many of us feel rushed, trying to meet deadlines, finishing off projects, and preparing for the holidays. Do you remember what December felt like when you were little? It was a time of wonder and expectation, a time to slow down and enjoy the small things: watching the first snowflakes fall, drinking hot chocolate, and admiring the Christmas decorations in your neighborhood, for example.

This month’s wallpapers post is a little reminder to treat yourself to some quiet moments like these in the midst of this end of the year rush. To refuel your batteries and gather some fresh inspiration. The wallpapers in this collection might be a good start.

As every month since more than nine years already, the wallpapers were designed by artists and designers from across the globe, and each one of them is available with and without a calendar for December 2019. For some extra variety, we also added a selection of wallpaper favorites from past editions at the end of this post. We wish you happy holidays and a lovely, and hopefully not too stressful, December!

Please note that:

  • All images can be clicked on and lead to the preview of the wallpaper,
  • We respect and carefully consider the ideas and motivation behind each and every artist’s work. This is why we give all artists the full freedom to explore their creativity and express emotions and experience through their works. This is also why the themes of the wallpapers weren’t anyhow influenced by us but rather designed from scratch by the artists themselves.

Submit your wallpaper

Do you have an idea for a wallpaper to welcome 2020? We are always looking for creative talent to be featured in our wallpapers posts. Don’t be shy, join in! →

Dear Moon, Merry Christmas

“Please visit Vladstudio website if you like my works!” — Designed by Vlad Gerasimov from Russia.

Dear Moon, Merry Christmas

Christmas Mood

Designed by MasterBundles from the United States.

Christmas Mood

The Month of Winter Songs

“Here comes the finale of a dazzling year. The month of snow with a tinge of warmth from the carols in the wind. All made merrier with the mistletoe, cakes and gifts. Let’s welcome the winter of our lives as spring is not far away now.” — Designed by Odoo Apps from India.

The Month of Winter Songs

The Camels Wish You A Merry Christmas!

“The year is coming to an end and we say good-bye with a very christmassy animal, the camel. We wish you a merry christmas and a happy new year.” — Designed by Veronica Valenzuela from Spain.

The Camels Wish You A Merry Christmas!

Hanukkah Candles

“Hanukkah is a joyous celebration of all that is important in life; family, freedom, and light. So we wanted to make a calendar that celebrated this Jewish holiday and what better way than by featuring the beautiful Hanukkiyah. Happy Hanukkah everyone!” — Designed by Ever Increasing Circles from the United Kingdom.

Hanukkah Candles

Happy Holidays!

Designed by Ricardo Gimenes from Sweden.

Happy Holidays!

Reindeers Go Rah-Rah!

“Reindeers go rah-rah when the season of snow is on the way. Let’s get excited as our beloved Santa comes with gifts for the tiny tots and fill the air with nothing but cheer! It’s Christmas time, y’all!” — Designed by Riddlebook from London.

Reindeers Go Rah-Rah!

Oldies But Goodies

Since the beginning of our monthly challenge, December and the holiday season have inspired so many designers to create a wallpaper. Below we collected some favorites from our archives that are just too good to be forgotten. Enjoy! (Please note that these designs don’t come with a calendar.)

Silver Winter

Designed by Violeta Dabija from Moldova.

Smashing Wallpaper - January 2011

Cardinals In Snowfall

“During Christmas season, in the cold, colorless days of winter, Cardinal birds are seen as symbols of faith and warmth! In the part of America I live in, there is snowfall every December. While the snow is falling, I can see gorgeous Cardinals flying in and out of my patio. The intriguing color palette of the bright red of the Cardinals, the white of the flurries and the brown/black of dry twigs and fallen leaves on the snow-laden ground, fascinates me a lot, and inspired me to create this quaint and sweet, hand-illustrated surface pattern design as I wait for the snowfall in my town!” — Designed by Gyaneshwari Dave from the United States.

Cardinals In Snowfall

Sweet Snowy Tenderness

“You know that warm feeling when you get to spend cold winter days in a snug, homey, relaxed atmosphere? Oh, yes, we love it too! It is the sentiment we set our hearts on for the holiday season, and this sweet snowy tenderness is for all of us who adore watching the snowfall from our windows. Isn’t it romantic?” — Designed by PopArt Studio from Serbia.

Sweet Snowy Tenderness

’Tis The Season Of Snow

“The tiny flakes of snow have just begun to shower and we know it’s the start of the merry hour! Someone is all set to cram his sleigh with boxes of love as kids wait for their dear Santa to show up! Rightly said, ’tis the season of snow, surprise and lots and lots of fun! Merry Christmas!” — Designed by Sweans Technologies from London.

’Tis The Season Of Snow

Abstract Winter

“Winter is cold and dark up here in the north.” Designed by Terese Brännström from Sweden.

Abstract winter

All That Belongs To The Past

“Sometimes new beginnings make us revisit our favorite places or people from the past. We don’t visit them often because they remind us of the past but enjoy the brief reunion. Cheers to new beginnings in the new year!” Designed by Dorvan Davoudi from Canada.

All That Belongs To The Past

All Of Them Lights

“I created this design in honor of the 9th of December, the day of lights.” — Designed by Mathias Geerts from Belgium.

All Of Them Lights

Tongue Stuck On Lamppost

Designed by Josh Cleland from the United States.

Smashing Wallpaper - december 11

Christmas Wreath

“Everyone is in the mood for Christmas when December starts. Therefore I made this Christmas wreath inspired wallpaper. Enjoy December and Merry Christmas to all!” — Designed by Melissa Bogemans from Belgium.

Christmas Wreath

Winter Garphee

“Garphee’s flufiness glowing in the snow.” Designed by Razvan Garofeanu from Romania.

Smashing Wallpaper - December 2012

Ice Flowers

“I took some photos during a very frosty and cold week before Christmas.” Designed by Anca Varsandan from Romania.

Smashing Wallpaper - january 10

Winter Morning

“Early walks in the fields when the owls still sit on the fences and stare funny at you.” — Designed by Bo Dockx from Belgium.

Winter Morning

Celebration Galore Is Here Again

“Christmas bells are swinging above the snow fields, we hear sweet voices ringing from lands of long ago… It’s time to count your blessings, sing your Christmas carols, open your gifts, and make a wish under the Christmas tree!” — Designed by Norjimm Pvt Ltd from India.

Celebration Galore Is Here Again

Surprise

“Surprise is the greatest gift which life can grant us.” — Designed by PlusCharts from India.

Surprise

Enchanted Blizzard

“A seemingly forgotten world under the shade of winter glaze hides a moment where architecture meets fashion and change encounters steadiness.” — Designed by Ana Masnikosa from Belgrade, Serbia.

Enchanted Blizzard

Expectation

“Blessed is he who expects nothing, for he shall never be disappointed.” — Designed by StarAdmin from India.

Expectation

Winter

“The winter solstice has always been special to me as a barren darkness that gives birth to a verdant future beyond imagination, a time of pain and withdrawal that produces something joyfully inconceivable, like a monarch butterfly masterfully extracting itself from the confines of its cocoon, bursting forth into unexpected glory. (Gary Zukav)” — Designed by Dipanjan Karmakar from India.

Winter

Merry Christmas

Designed by Delphine Pagès from France.

Christmas Wallpaper — Merry Christmas

Season Of Joy

Designed by Antun Hirsman from Croatia.

Season Of Joy

Christmas Fail

Designed by Doud – Elise Vanoorbeek from Belgium.

Christmas Wallpaper — Christmas Fail

The Matterhorn

“Christmas is always such a magical time of year so we created this wallpaper to blend the majestry of the mountains with a little bit of magic.” — Designed by Dominic Leonard from the United Kingdom.

Christmas Wallpaper — The Matterhorn

Season’s Greetings From Australia!

Designed by Tazi Designs from Australia.

Christmas Wallpaper — Season’s Greetings From Australia!

December Deer

“I love the simplicity of the deer head silhouette trend with just a touch of hand-drawn detail. Happy December my dears!” — Designed by Jordan Thoma from the United States.

December deer

Father Frost

Designed by Cheloveche.ru from Russia.

Smashing Wallpaper - december 11

Winter Solstice

“In December there’s a winter solstice; which means that the longest night of the year falls in December. I wanted to create the feeling of solitude of the long night into this wallpaper.” — Designed by Alex Hermans from Belgium.

Winter Solstice

Have A Minimal Christmas

“My brother-in-law has been on a design buzzword kick where he calls everything minimal, to the point where he wishes people, “Have a minimal day!” I made this graphic as a poster for him.” — Designed by Danny Gugger from Madison, Wisconsin, USA.

Have a Minimal Christmas

Delicate Frost

“Don’t let Jack Frost nip too much at your nose but do let him decorate your windows!” Designed by Tirelessweaver from Canada.

Smashing Desktop Wallpapers - January 2012

The Deer In My Garden

“Every year at the onset of winter, a deer appears in my garden looking for food. I usually catch it early in the morning and we’ll be exchanging glances through the patio doors.” — Designed by Andrea Ludszeweit from Germany.

The deer in my garden

Don’t Stop

“It’s been such an incredible year for space; I wanted to celebrate that with a simple wallpaper to keep everyone inspired this month. The year isn’t over yet — don’t stop pushing yourself!” — Designed by Shawna Armstrong from the United States.

Don’t Stop

The Twelve Days Of Christmas

“This wallpaper celebrates the classic carol ‘The Twelve Days of Christmas’. Each day is represented with a cheerful illustration representing gifts.” — Designed by Daphne Firos from Cleveland.

The 12 Days of Christmas

It’s In The Little Things

Designed by Thaïs Lenglez from Belgium.

It's in the little things

Decemberist

“‘December is plowing yet. When the smoke-clouds break, high in the sky shines a field as wide as the world. There he toils for the Kingdom of Heaven’s sake. Ah, he is taller than clouds of the little earth. Only the congress of planets is over him, and the arching path where new sweet stars have birth. Wearing his peasant dress, his head bent low, December, that angel of Peace, is plowing yet; Forward, across the field, his horses go.’ (Based on Chinese Nightingale, 1917).” Designed by Dynomite from Germany.

Smashing Wallpaper - December 2012

Best Friends

“Best friends posing for a photo.” Designed by Nenad S. Lazich from Serbia.

Smashing Wallpaper - December 2012

Catch Your Perfect Snowflake

“This time of year people tend to dream big and expect miracles. Let your dreams come true!” Designed by Igor Izhik from Canada.

Catch Your Perfect Snowflake

Cool Winter

“Wanted to showcase the cool, crisp colors and give an overall feeling of winter. Designed by Matt Noa from the United States.

Cool Winter

Robin Bird

“I have chosen this little bird in honor of my grandfather, who passed away. He was fascinated by nature, especially birds. Because of him, I also have a fascination with birds. When I think of winter, I think of the birds, flying around searching for food. And why a robin? Because it is a cute little bird, who is also very recognizable.” — Designed by Engin Seline from Belgium.

Robin Bird

Join In Next Month!

Thank you to all designers for their participation. Join in next month!

Simplified Fluid Typography

Fluid typography is the idea that font-size (and perhaps other attributes of type, like line-height) change depending on the screen size (or perhaps container queries if we had them).

The core trickery comes from viewport units. You can literally set type in viewport units (e.g. font-size: 4vw), but the fluctuations in size are so extreme that it’s usually undesirable. That’s tampered by doing something like font-size: calc(16px + 1vw). But while we’re getting fancy with calculations anyway, the most common implementation ended up being an equation to calculate plain English:

I want the type to go between being 16px on a 320px screen to 22px on a 1000px screen.

Which ended up like this:

html { font-size: 16px;
}
@media screen and (min-width: 320px) { html { font-size: calc(16px + 6 * ((100vw - 320px) / 680)); }
}
@media screen and (min-width: 1000px) { html { font-size: 22px; }
} 

That’s essentially setting a minimum and maximum font size so the type won’t shrink or grow to anything too extreme. “CSS locks” was a term coined by Tim Brown.

Minimum and maximum you say?! Well it so happens that functions for these have made their way into the CSS spec in the form of min() and max().

So we can simplify our fancy setup above with a one-liner and maintain the locks:

html { font-size: min(max(16px, 4vw), 22px);
}

We actually might want to stop there because even though both Safari (11.1+) and Chrome (79+) support this at the current moment, that’s as wide as support will get today. Speaking of which, you’d probably want to slip a font-size declaration before this to set an acceptable fallback value with no fancy functions.

But as long as we’re pushing the limits, there is another function to simplify things even more: clamp()! Clamp takes three values, a min, max, and a flexible unit (or calculation or whatever) in the middle that it will use in case the value is between the min and max. So, our one-liner gets even smaller:

body { font-size: clamp(16px, 4vw, 22px);
} 

That’ll be Chrome 79+ (which doesn’t hasn’t even dropped to stable but will very soon).

Uncle Dave is very happy that FitText is now a few bytes instead of all-of-jQuery plus 40 more lines. Here is us chucking CSS custom properties at it:

See the Pen
FitText in CSS with clamp()
by Dave Rupert (@davatron5000)
on CodePen.

The post Simplified Fluid Typography appeared first on CSS-Tricks.

Testing React Hooks With Enzyme and React Testing Library

As you begin to make use of React hooks in your applications, you’ll want to be certain the code you write is nothing short of solid. There’s nothing like shipping buggy code. One way to be certain your code is bug-free is to write tests. And testing React hooks is not much different from how React applications are tested in general.

In this tutorial, we will look at how to do that by making use of a to-do application built with hooks. We’ll cover writing of tests using Ezyme and React Testing Library, both of which are able to do just that. If you’re new to Enzyme, we actually posted about it a little while back showing how it can be used with Jest in React applications. It’s not a bad idea to check that as we dig into testing React hooks.

Here’s what we want to test

A pretty standard to-do component looks something like this:

import React, { useState, useRef } from "react";
const Todo = () => { const [todos, setTodos] = useState([ { id: 1, item: "Fix bugs" }, { id: 2, item: "Take out the trash" } ]); const todoRef = useRef(); const removeTodo = id => { setTodos(todos.filter(todo => todo.id !== id)); }; const addTodo = data => { let id = todos.length + 1; setTodos([ ...todos, { id, item: data } ]); }; const handleNewTodo = e => { e.preventDefault(); const item = todoRef.current; addTodo(item.value); item.value = ""; }; return ( <div className="container"> <div className="row"> <div className="col-md-6"> <h2>Add Todo</h2> </div> </div> <form> <div className="row"> <div className="col-md-6"> <input type="text" autoFocus ref={todoRef} placeholder="Enter a task" className="form-control" data-testid="input" /> </div> </div> <div className="row"> <div className="col-md-6"> <button type="submit" onClick={handleNewTodo} className="btn btn-primary" > Add Task </button> </div> </div> </form> <div className="row todo-list"> <div className="col-md-6"> <h3>Lists</h3> {!todos.length ? ( <div className="no-task">No task!</div> ) : ( <ul data-testid="todos"> {todos.map(todo => { return ( <li key={todo.id}> <div> <span>{todo.item}</span> <button className="btn btn-danger" data-testid="delete-button" onClick={() => removeTodo(todo.id)} > X </button> </div> </li> ); })} </ul> )} </div> </div> </div> );
};
export default Todo; 

Testing with Enzyme

We need to install the packages before we can start testing. Time to fire up the terminal!

npm install --save-dev enzyme enzyme-adapter-16 

Inside the src directory, create a file called setupTests.js. This is what we’ll use to configure Enzyme’s adapter.

import Enzyme from "enzyme";
import Adapter from "enzyme-adapter-react-16";
Enzyme.configure({ adapter: new Adapter() }); 

Now we can start writing our tests! We want to test four things:

  1. That the component renders
  2. That the initial to-dos get displayed when it renders
  3. That we can create a new to-do and get back three others
  4. That we can delete one of the initial to-dos and have only one to-do left

In your src directory, create a folder called __tests__ and create the file where you’ll write your Todo component’s tests in it. Let’s call that file Todo.test.js.

With that done, we can import the packages we need and create a describe block where we’ll fill in our tests.

import React from "react";
import { shallow, mount } from "enzyme";
import Todo from "../Todo"; describe("Todo", () => { // Tests will go here using `it` blocks
});

Test 1: The component renders

For this, we’ll make use of shallow render. Shallow rendering allows us to check if the render method of the component gets called — that’s what we want to confirm here because that’s the proof we need that the component renders.

it("renders", () => { shallow(<Todo />);
});

Test 2: Initial to-dos get displayed

Here is where we’ll make use of the mount method, which allows us to go deeper than what shallow gives us. That way, we can check the length of the to-do items.

it("displays initial to-dos", () => { const wrapper = mount(<Todo />); expect(wrapper.find("li")).toHaveLength(2);
});

Test 3: We can create a new to-do and get back three others

Let’s think about the process involved in creating a new to-do:

  1. The user enters a value into the input field.
  2. The user clicks the submit button.
  3. We get a total of three to-do items, where the third is the newly created one.
it("adds a new item", () => { const wrapper = mount(<Todo />); wrapper.find("input").instance().value = "Fix failing test"; expect(wrapper.find("input").instance().value).toEqual("Fix failing test"); wrapper.find(' ||').simulate("click"); expect(wrapper.find("li")).toHaveLength(3); expect( wrapper .find("li div span") .last() .text() ).toEqual("Fix failing test");
});

We mount the component then we make use of find() and instance() methods to set the value of the input field. We assert that the value of the input field is set to “Fix failing test” before going further to simulate a click event, which should add the new item to the to-do list.

We finally assert that we have three items on the list and that the third item is equal to the one we created.

Test 4: We can delete one of the initial to-dos and have only one to-do left

it("removes an item", () => { const wrapper = mount(<Todo />); wrapper .find("li button") .first() .simulate("click"); expect(wrapper.find("li")).toHaveLength(1); expect(wrapper.find("li span").map(item => item.text())).toEqual([ "Take out the trash" ]);
});

In this scenario, we return the to-do with a simulated click event on the first item. It’s expected that this will call the removeTodo() method, which should delete the item that was clicked. Then we’re checking the numbers of items we have, and the value of the one that gets returned.

The source code for these four tests are here on GitHub for you to check out.

Testing With react-testing-library

We’ll write three tests for this:

  1. That the initial to-do renders
  2. That we can add a new to-do
  3. That we can delete a to-do

Let’s start by installing the packages we need:

npm install --save-dev @testing-library/jest-dom @testing-library/react

Next, we can import the packages and files:

import React from "react";
import { render, fireEvent } from "@testing-library/react";
import Todo from "../Todo";
import "@testing-library/jest-dom/extend-expect"; test("Todo", () => { // Tests go here
}

Test 1: The initial to-do renders

We’ll write our tests in a test block. The first test will look like this:

it("displays initial to-dos", () => { const { getByTestId } = render(<Todo />); const todos = getByTestId("todos"); expect(todos.children.length).toBe(2);
});

What’s happening here? We’re making use of getTestId to return the node of the element where data-testid matches the one that was passed to the method. That’s the <ul> element in this case. Then, we’re checking that it has a total of two children (each child being a <li> element inside the unordered list). This will pass as the initial to-do is equal to two.

Test 2: We can add a new to-do

We’re also making use of getTestById here to return the node that matches the argument we’re passing in.

it("adds a new to-do", () => { const { getByTestId, getByText } = render(<Todo />); const input = getByTestId("input"); const todos = getByTestId("todos"); input.value = "Fix failing tests"; fireEvent.click(getByText("Add Task")); expect(todos.children.length).toBe(3);
});

We use getByTestId to return the input field and the ul element like we did before. To simulate a click event that adds a new to-do item, we’re using fireEvent.click() and passing in the getByText() method, which returns the node whose text matches the argument we passed. From there, we can then check to see the length of the to-dos by checking the length of the children array.

Test 3: We can delete a to-do

This will look a little like what we did a little earlier:

it("deletes a to-do", () => { const { getAllByTestId, getByTestId } = render(<Todo />); const todos = getByTestId("todos"); const deleteButton = getAllByTestId("delete-button"); const first = deleteButton[0]; fireEvent.click(first); expect(todos.children.length).toBe(1);
});

We’re making use of getAllByTestId to return the nodes of the delete button. Since we only want to delete one item, we fire a click event on the first item in the collection, which should delete the first to-do. This should then make the length of todos children equal to one.

These tests are also available on GitHub.

Linting

There are two lint rules to abide by when working with hooks:

Rule 1: Call hooks at the top level

…as opposed to inside conditionals, loops or nested functions.

// Don't do this!
if (Math.random() > 0.5) { const [invalid, updateInvalid] = useState(false);
}

This goes against the first rule. According to the official documentation, React depends on the order in which hooks are called to associate state and the corresponding useState call. This code breaks the order as the hook will only be called if the conditions are true.

This also applies to useEffect and other hooks. Check out the documentation for more details.

Rule 2: Call hooks from React functional components

Hooks are meant to be used in React functional components — not in React’s class component or a JavaScript function.

We’ve basically covered what not to do when it comes to linting. We can avoid these missteps with an npm package that specifically enforces these rules.

npm install eslint-plugin-react-hooks --save-dev

Here’s what we add to the package’s configuration file to make it do its thing:

{ "plugins": [ // ... "react-hooks" ], "rules": { // ... "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn" }
}

If you are making use of Create React App, then you should know that the package supports the lint plugin out of the box as of v3.0.0.

Go forth and write solid React code!

React hooks are equally prone to error as anything else in your application and you’re gonna want to ensure that you use them well. As we just saw, there’s a couple of ways we can go about it. Whether you use Enzyme or You can either make use of enzyme or React Testing Library to write tests is totally up to you. Either way, try making use of linting as you go, and no doubt, you’ll be glad you did.

The post Testing React Hooks With Enzyme and React Testing Library appeared first on CSS-Tricks.

Black Friday 2019: Support Indie Makers

Black Friday 2019: Support Indie Makers

Black Friday 2019: Support Indie Makers

Rachel Andrew

Every time I have checked my email over the last two weeks, it has been full of Black Friday deals. We will get a short respite before the New Year offers start to roll in. I like a bargain as much as anyone, however, I think that plenty of sites will be covering the best offers on electronics and tech.

I thought we would do something different this year at Smashing. I’ve launched a number of independent products over the years — downloadable software, software as a service, self-published books, and a course. I know how difficult it can be to get the word out about your products when self-funding, so I thought we could give a boost to all the indie makers out there and feature some of their products.

We asked the Smashing community for their suggestions, and so here is a list covering pretty much every kind of product you can imagine. I hope you can find something you need in these, and help support these hard-working folks.

Search by category:

  1. Books
  2. Gifts, Artwork, And Posters
  3. Printed Magazines
  4. Courses And Training
  5. Software And Tools
  6. Other

Books

A collection of independently published books, and small publishers, with a shoutout to a very special project.

Oddly Amazing Animals

Oddly Amazing AnimalsA book project started by the talented Cindy Li, who was a friend to many of us in the web community. After Cindy passed away, her friends got together to finish the book, and all proceeds will go to Cindy’s two young sons.

The Power Of Digital Policy

The Power Of Digital PolicyYou don’t need an army of consultants to help you protect your organization from brand degradation and reputational threats. This practical guide by Kristina Podnar will guide you in minimizing risks and maximizing opportunities.

The Game Engine Black Book

The Game Engine Black BookThis book details techniques such as raycasting, compiled scalers, deferred rendition, VGA Mode-Y, linear feedback shift register, fixed point arithmetic, and many others tricks. Fabien Sanglar also went into much detail to describe the hardware of 1991, and has released the source code under GPL license.

404 Ink

404 InkFounded by two publishing freelancers, Heather McDaid and Laura Jones, this publisher has one goal: supporting careers of new and emerging writers — and making as much noise as possible about each.

A Book Apart

A Book ApartBooks available in two formats (Standards and Briefs) on topics ranging from technical to theory: responsive web design, Git, and JavaScript to content strategy, design principles, management, and more. For people who design, write, and code.

Smashing Books

Smashing BooksOur very own Smashing books aim to deliver in-depth knowledge and expertise shared by experts and practitioners from the industry. Our most recent one, Inclusive Components, explores bulletproof solutions for building accessible interfaces.

Gifts, Artwork, And Posters

If you are finding gifts for friends and family for the holidays, why not support these independent makers.

DoodleCats

DoodleCatsTopple the cat approves of this website of cat-themed products! Created by artist Beth Wilson, you’ll find a wide range of cute cat-themed greetings cards, gifts and accessories.

Seb Lester

Seb LesterMost of us have already heard of Seb Lester, but you’ll be thrilled to know that his beautiful hand-lettered work is also available to call your own.

Jessica Hische

Jessica HischeJessica is a lettering artist who has been creating custom lettering artwork for established brands, classic books and postage stamps for over the past ten years. You’ll find a wonderful collection of prints, cards and pins on her site.

Draplin Design Co. Merch

Draplin Design Co. MerchAaron James Draplin is the founder of the Draplin Design Co., based in Portland, Oregon. You’ll find a range of fun items on his site.

HeyShop

HeyShopThe graphic design and illustration studio Hey launched an online shop back in 2014. Since then, they’ve been sharing their personal creations with the public.

Print Workers Barcelona

Print Workers BarcelonaA nice and lovely graphic shop that focuses on handmade, limited, signed, numbered and self-published production. Artists who mainly use screen printing without forgetting other techniques such as risograph printing or letterpress. Graphic work of more than 100 international and local artists. All at affordable and real prices.

The Oh No Shop

The Oh No ShopBrought to life by Alex Norris, we’re sure that you’ll be all smiles when you check out his prints, pins and more.

Levens

LevensIf you’re a fan of jewels, check out these beautifully handmade ones done in ceramic, silver and gold. Designed by Mar del Hoyo from Barcelona, Spain.

Cristina Junquero

Cristina JunqueroInspired by Andalusian religious imaginary and classical jewellery, the work of Cristina Junquero revisits tradition to bring something new. Her studio is set in Barcelona.

Casa Atlántica

Casa AtlánticaSince 2014, Casa Atlântica works to give value to trades that are gradually being lost: their objects are born in villages of Galicia and Portugal from the hands of artisans who, with materials such as ceramics, wicker or wood, give life to their designs.

Après Ski

Après SkiEstablished in 2009, this accessories and objects studio creates designs that are inspired from the observation of different cultures and traditions — seeking people and places authenticity through books and travels.

Pimoroni

PimoroniFounded in 2012 by Jon Williamson and Paul Beech, Pimoroni makes tech treasure for tinkerers.

Varianto:25

Varianto:25A small startup based in Bulgaria that create fun and innovative products for developers worldwide.

Ysolda

YsoldaAn online store for knitters, based in Edinburgh.

Printed Magazines

As I know from launching our own print magazine here at Smashing Magazine this year, creating a print magazine requires a huge amount of work. Here are some of your favorites.

Offscreen Magazine

Offscreen MagazineOffscreen is an independent print magazine that examines how we shape technology and how technology shapes us. Offscreen Magazine is a favorite of many Smashing readers. Also check out the Dense Discovery email newsletter (I always find something new there).

Bubblesort Zines

Bubblesort ZinesZines about computer science, for ages 8-100! Each zine focuses on one concept and is filled with comics, diagrams, stories, examples, and exercises.

WizardZines

WizardZinesZines by Julia Evans that are aimed at working programmers who want to know how to use grep / tcpdump / strace in a fun way. (A lot of them are focused on systems/Linux concepts.)

Like The Wind

Like The WindThis running magazine is a favorite of mine. Beautifully printed, with inspiring stories from the world of runners and running.

Courses And Training

3D Fundamentals

3D Fundamentals3D is a creative playground for designers, yet still uncharted territory for most of us. 3D Fundamentals teaches you shape, form, lighting, color, and animation in a beginner-friendly course.

Every Layout

Every LayoutIf you find yourself wrestling with CSS layout, Every Layout is for you. Through a series of simple, composable layouts, you will learn how to better harness the built-in algorithms that power browsers and CSS.

Terminal Training

Terminal TrainingWorking with Terminal can be daunting. This video course wants to cure you from any fear of the terminal. For designers, new developers, UX, UI, product owners, and anyone who’s been asked to “just open the terminal”.

Universal JavaScript with Next.js

Universal JavaScript with Next.jsIf you’re tired of configuration, build tools, spagetti code and want to focus on building amazing web apps with the latest features, this complete video course will get you fit for building web apps with Next.js for React.

The CSS Workshop

The CSS WorkshopLearn CSS layout through a series of video tutorials. Straightforward and practical examples help you banish layout confusion for good.

Software And Tools

A whole selection of interesting products and tools. Many of these have free plans. If you love one of these products, however, do consider signing up for the paid version if you can. Bootstrapped products need sales, or they go away!

Better Blocker

Better BlockerA privacy tool for Safari on iPhone, iPad, and Mac. Launched by Aral Balkan and Laura Kalbag, the aim is to protect users from behavioural ads and companies that track and profile folks on the web.

Polypane

PolypaneBuilt for designers and developers, the browser Polypane lets you create sites and apps that work for everyone. Features include multiple synced viewports for responsive design, visual impairment simulators, built-in accessibility testing tools, live refreshing, layout debugging and screenshotting.

Common Ninja

Common NinjaThe Common Ninja team creates plugins with the purpose to help web designers, developers, and site owners to upgrade and color their website with zero effort, time, and knowledge.

Helperbird

HelperbirdThe browser extension wants to bring the benefits of accessbility and customization to everyone, with features such as dyslexia fonts, changing the font and background color, text to speech, overlays, dyslexia rulers and more to make the web accessible to your needs.

Lunch Money

Lunch MoneyNo matter where you are in the world, Lunch Money keeps track of every dollar, euro, and yen spent. At the end of the day, they add it all up in your currency of choice so that you stay on top of your spendings without doing the maths.

Timemator

TimematorTimemator automatically captures everything you do on your Mac. You define the rules, and once you open your working file or application, Timemator will start the timer for you automatically.

Exist

ExistBy combining data from services you already use, Exist can help you understand what makes you more happy, productive, and active. Bring your activity from your phone or fitness tracker and add other services like your calendar for greater context on what you’re up to.

Proxyman

ProxymanProxyman is a native, high-performance macOS application, which enables developers to observe and manipulate HTTP/HTTPS requests. Intuitive and friendly.

Standard Notes

Standard NotesSometimes all you need is a reliable and fuss-free tool to jot down your thoughts and ideas. Standard Notes is just that, a free, open-source, and completely encrypted notes app.

Leave Me Alone

Leave Me AloneUnsubscribing from the emails you don’t want to receive any longer can be time-consuming. Leave Me Alone shows you all of your subscription emails in one place so that you can unsubscribe from them with a single click.

Readermode

ReadermodeYou’re getting distracted easily when you read? Reader Mode instantly removes clutter, ads, and distractions from any article. Dyslexia support is built in, too.

Fathom

FathomStop scrolling through pages of reports and collecting gobs of personal data about your visitors, both of which you probably don’t need. Fathom is a simple and private website analytics platform that lets you focus on what’s important: your business.

Buttondown

ButtondownButtondown is a small elegant tool for producing newsletters. The minimalist interface makes it easy to write great emails; the automation acts like the editorial assistant you wish you had; and the portable subscription widget helps grow your audience from anywhere.

Carrd.co

Carrd.coNo matter if it’s a personal profile, a landing page to capture emails, or something a bit more elaborate, Carrd lets you create simple and responsive one-page sites for pretty much anything.

Placid

PlacidFacebook, Twitter, Pinterest — all of them have different requirements when it comes to social share images. To save you time, Placid creates your social share images automatically. You define a template once, the tool does the rest.

Calibre

CalibreCalibre helps you monitor and audit web performance and make meaningful improvements where it matters. You can simulate real-world conditions to understand what your audience is experiencing, see the impact of third-party code, receive monthly reports on crucial metrics without having to spend hours on distilling performance data, and much more.

Transistor

TransistorHave you ever considered starting your own podcast? Transistor helps you with the rather boring part, storing your MP3 files, generating your RSS feed, hosting your podcast’s website, and distributing your show to Apple Podcasts, Spotify, and more.

Kirby

KirbyKirby is a file-based CMS for building your own ideal interface. Combine forms, galleries, articles, spreadsheets and more into an amazing editing experience.

Perch

PerchThe Really Little CMS. Used by thousands of happy customers around the world, Perch does not dictate your front-end code but lets you bring your own code to your project.

Statamic

StatamicStatamic cuts out the database and creates a faster, more productive way for you to build, manage, and version control beautifully creative, bespoke websites.

Other Things

A bunch of things that didn’t really fit into any other category.

rooki.design

rooki.designAn online magazine for design students and free design awards, Rookie was born out of the frustration in finding good, free resources for design students. Now, you can find everything you need in one single place.

Femtech Insider

Femtech InsiderStay up-to-date and read about the latest industry trends, while you learn more about founders, companies, organizations and investors at the intersection of tech and women’s health.

Rapscallion Soda

Rapscallion Soda“We are living in a world today where lemonade is made from artificial flavours & furniture polish is made with real lemons.” The handmade, bootstrapped soda company from Glasgow wants to change that.

Tech ladies

Tech ladiesTech Ladies connects you with the best jobs and opportunities in tech. Join the community or post to their job board if you are looking for employees.

Front-End Challenges Club

Front-End Challenges ClubDo you want to put your front-end skills to the test? The Front-End Challenges Club gives you a new fun challenge to master every two weeks.

Diversify Tech

Diversify TechDiversify Tech connects underrepresented people in tech. Once a week, they’ll send you scholarships, events, job opportunities, and more.

With Jack

With JackWith Jack is all about insurance for freelance creatives, giving designers, developers, illustrators, and other web professionals the insurance they need. No endless features or stale service but one solid policy and the personal touch.

Find Support If You Are An Indie Maker

There are some excellent communities that seek to support bootstrapped businesses, sole founders and small teams. Check these out to find interesting products — or to get help in shipping your own.

  • IndieHackers: Work together to build profitable online businesses.
  • Makerlog: A collaborative task log that helps over 3000+ creators get things done.
  • WIP: Maker Community.

Add Your Favorites To The Comments!

Did we miss one of your favorite independent products? If so, please add a link in the comments, and don’t forget to let us know what it is and why you love it, too!

Smashing Editorial (il, cm)
10 Popular Design Trends It’s Time to Let Die

If you’ve ever moved from one home to another, you know how difficult it can be to get rid of things you’ve owned for years. While digging through your closet, you find an old pair of pants you used to wear all the time despite the growing holes in the knees. But you tell yourself, “Maybe I’ll wear them around the house when it gets warmer” or “I bet the grunge look will come back in style”.

It’s easy to make these kinds of justifications in web design and development, too. You think:

“I’ve used the keyword meta tag for as long as I can remember. What can it hurt to keep doing so?”

Similar to how your clothes may become outdated or your appliances obsolete over time, the same thing happens with design and development trends. Rather than hold onto techniques that no longer serve you and only add more to your workload, it’s a much better idea to clear them out and make way for modern trends that’ll have a greater impact.

10 Popular Design Trends It’s Time to Let Die

When we talk about outdated web design trends, we’re not just talking about ones that have been obsolete for years. We’re also referring to trends and techniques that we know for a fact compromise the user experience and need to go away ASAP.

1. Cheesy Stock Photos

There’s nothing inherently bad about using stock photos. Many clients don’t have the budgets or wherewithal to create their own company photos and stock photos are a viable alternative.

That said, there was a time when “bad” (i.e. super cheesy and unrealistic) stock photos were all the rage. Even today, you’ll find websites that use these kinds of photos because there’s still an assumption that two people shaking hands in a well-lit conference room signals trust. (It doesn’t.)

Image via DepositPhotos.

2. Hero Sliders

Image slider technology was pretty great in its heyday. It allowed web designers to conserve space while displaying a number of promotional offers at once. In addition to sliders often slowing down page speeds, they also have a tendency to slow users down as they distract them from moving onto other parts of a website.

Verizon Wireless, for instance, has a great example of a strong but simple hero image design in 2019:

This is vastly different from the image slider it used back in 2013:

For the most part, we’ve learned to be more efficient with this space, though there are still some websites that can’t make up their minds about which offer to show above the fold… which only makes it more difficult for visitors to decide next steps. Take the initiative and do it for them with a single hero banner.

3. Autoplay

It’s not common to find websites with background audio, let alone autoplay audio, these days. That said, what you do occasionally find are websites that automatically play videos or ads with audio. Needless to say, this needs to stop. If your video (or audio) players don’t allow your visitors to take control of when they start, change that up now.

4. The 3-Click Rule

Over the years, web designers have looked for ways to decrease friction in the user experience. The three-click rule was meant to be one of the ways to do this. However, according to a recent report from the Nielsen Norman Group, there’s never been any data to back up this claim:

“In fact, a study by Joshua Porter has debunked it; the study showed that user dropoff does not increase when the task involves more than 3 clicks, nor does satisfaction decrease. Limiting interaction cost is indeed important, but the picture is more complicated than simply counting clicks and having a rule of thumb for the maximum number allowed.”

Rather than minimize for minimization’s sake, consider the complexity of the task or funnel you’re designing when determining quantity of steps.

5. (External) Links That Open in the Same Tab

There are a number of reasons to add links to your content: for navigational purposes, promotional purposes, and referential purposes. But when you add a hyperlink to your text, consider the following: Is it okay if the link directs visitors to a page in this same browser tab?

External links, for instance, should always open in a new browser tab. Your goal in designing a website is to get more visitors to convert. Letting an external link replace your website in the open tab will only decrease the chances of that happening. In some cases, internal links shouldn’t be opened in the same tab either. So, be sure to think about this the next time you add a link to your site.

6. Non-Traditional Scrolling

Although we’ve become accustomed to swipe gestures in mobile apps, horizontal and other non-traditional scrolling isn’t something that’s caught on with websites. While it’s definitely a design trend that helped many businesses set themselves apart from the pack a few years back, it’s just too gimmicky to use these days.

Robby Leonardi’s interactive resume website was one of the first I remember seeing and it was a brilliant way to capture attention — especially from those of us who grew up with Mario.

But today? Any sort of non-traditional scrolling is just impractical and unnecessary. Even Robby’s current website has broken up this side-scrolling design and turned it into a vertical-scrolling page:

If you want to keep visitors engaged with your website in this day and age, don’t make them figure out how to scroll through your website. 

7. Keyword Meta Tag

For years (we’re talking nearly a decade), the keyword meta tag has not been supported by popular search engines. Despite knowing that the meta tag is useless, some designers still take the time to add it in. But why bother if it’s an extra step that gets you nothing in return?

8. Bad Pop-ups

Although pop-ups have undergone an evolution over the years — from the super-annoying pop-up ads that appeared outside the browser to the ever-present privacy notices we now see thanks to GDPR. While there is certainly some value in using pop-ups on a website, there are just too many kinds of bad pop-ups that need to disappear.

“Bad” pop-ups are ones that:

  • Show up too early on a website (like the second someone enters it);
  • Appear too many times during a single or return visit;
  • Send users to Facebook Messenger to collect their lead magnet and then bombard them with messages there;
  • Contain two buttons. Users that accept the offer, get a friendly message. Those that don’t are served up aggressive or shame-inducing language;
  • Repeat an offer that’s already designed into the website as a promotional banner.

9. Slow-Loading Websites

Mobile websites are notoriously difficult to optimize for speed when compared to their desktop counterparts. Unlike in years past where you could’ve rationalized away speed optimizations for mobile, today, it needs to be a priority with Google’s mobile-first indexing. PWAs are one way to give your mobile site an instant speed boost.

On a related note, by designing a PWA instead of a mobile-responsive website, you’d be able to cater to users with poor or no wi-fi connectivity — a segment that’s often been overlooked in web design.

10. Flash

I cannot believe I’m having to include this last one in 2019, but it seems there are still websites using the Flash Player.

Adobe has already told us that it would be cutting support for Flash next year. Web browsers are starting to remove their support for Flash players as well. And good riddance. Flash has long had issues with security flaws and usability issues.

If you’re trying to hold out on this (or your clients are dragging their feet), keep in mind that this is what visitors will see on many browsers in 2020 and beyond:

Bottom line: If the creator of Flash is pulling support, you need to do the same for any of your websites that still use it.

Wrap-Up

It’s easy to get wrapped up in what the next big thing is in web design — AR tech, typography trends, color gradients, etc. But what about all of those trends and techniques that have become a habit over the years?

Rather than hold onto outdated design strategies that will only hinder your progress as a web designer and hold your clients’ websites back, start shedding these obsolete (or soon-to-be obsolete) practices now.

Source
p img {display:inline-block; margin-right:10px;}
.alignleft {float:left;}
p.showcase {clear:both;}
body#browserfriendly p, body#podcast p, div#emailbody p{margin:0;}

Web Scraping Made Simple With Zenscrape

Web scraping has always been taken care of by actual developers, since a lot of coding, proxy management and CAPTCHA-solving is involved. However, the scraped data is very often needed by people that are non-coders: Marketers, Analysts, Business Developers etc.

Zenscrape is an easy-to-use web scraping tool that allows people to scrape websites without having to code.

Let’s run through a quick example together:

Select the data you need

The setup wizard guides you through the process of setting up your data extractor. It allows you to select the information you want to scrape visually. Click on the desired piece of content and specify what type of element you have. Depending on the package you have bought (they also offer a free plan), you can select up to 30 data elements per page.

The scraper is also capable of handling element lists.

Schedule your extractor

Perhaps, you want to scrape the selected data at a specific time interval. Depending on your plan, you can choose any time span between one minute to one hour. Also, decide what is supposed to happen with the scraped data after it has been gathered.

Use your data

In this example, we have chosen the .csv-export method and have selected a 10 minute scraping interval. Our first set of data should be ready by now. Let’s take a look:

Success! Our data is ready for us to be downloaded. We can now access all individual data sets or download all previously gathered data at once, in one file.

Need more flexibility?

Zenscrape also offers a web scraping API that returns the HTML markup of any website. This is especially useful for complicated scraping projects, that require the scraped content to be integrated into a software application for further processing.

Just like the web scraping suite, the API does not forward failed requests and takes care of proxy management, Capotcha-solving and all other maintenance tasks that are usually involved with DIY web scrapers.

Since the API returns the full HTML markup of the related website, you have full flexibility in terms of data selection and further processing.

Try Zenscrape

The post Web Scraping Made Simple With Zenscrape appeared first on CSS-Tricks.

Abstracting WordPress Code To Reuse With Other CMSs: Implementation (Part 2)

Abstracting WordPress Code To Reuse With Other CMSs: Implementation (Part 2)

Abstracting WordPress Code To Reuse With Other CMSs: Implementation (Part 2)

Leonardo Losoviz

In the first part of this series, we learned the key concepts to build an application that is as CMS-agnostic as possible. In this second and final part, we will proceed to abstract a WordPress application, making its code ready to be used with Symfony components, Laravel framework, and October CMS (which is based on Laravel).

Accessing Services

Before we start abstracting the code, we need to provide the layer of dependency injection to the application. As described in the first part of this series, this layer is satisfied through Symfony’s DependencyInjection component. To access the defined services, we create a class ContainerBuilderFactory which simply stores a static instance of the component’s ContainerBuilder object:

use Symfony\Component\DependencyInjection\ContainerBuilder; class ContainerBuilderFactory { private static $instance; public static function init() { self::$instance = new ContainerBuilder(); } public static function getInstance() { return self::$instance; }
}

Then, to access a service called "cache", the application requests it like this:

$cacheService = ContainerBuilderFactory::getInstance()->get('cache');
// Do something with the service
// $cacheService->...

Abstracting WordPress Code

We have identified the following pieces of code and concepts from a WordPress application that need be abstracted away from WordPress’s opinionatedness:

  • accessing functions
  • function names
  • function parameters
  • states (and other constant values)
  • CMS helper functions
  • user permissions
  • application options
  • database column names
  • errors
  • hooks
  • routing
  • object properties
  • global state
  • entity models (meta, post types, pages being posts, and taxonomies —tags and categories—)
  • translation
  • media.

Let’s proceed to abstract them, one by one.

Note: For ease of reading, I have omitted adding namespaces to all classes and interfaces throughout this article. However, adding namespaces, as specified in PHP Standards Recommendation PSR-4, is a must! Among other advantages, the application can then benefit from autoloading, and Symfony’s dependency injection can rely on automatic service loading as to reduce its configuration to the bare minimum.

Accessing functions

The mantra “code against interfaces, not implementations” means that all those functions provided by the CMS cannot be accessed directly anymore. Instead, we must access the function from a contract (an interface), on which the CMS function will simply be the implementation. By the end of the abstraction, since no WordPress code will be referenced directly anymore, we can then swap WordPress with a different CMS.

For instance, if our application accesses function get_posts:

$posts = get_posts($args);

We must then abstract this function under some contract:

interface PostAPIInterface
{ public function getPosts($args);
}

The contract must be implemented for WordPress:

class WPPostAPI implements PostAPIInterface
{ public function getPosts($args) { return get_posts($args); }
}

A service "posts_api" must be added to the dependency injection services.yaml configuration file, indicating which class resolves the service:

services: posts_api: class: \WPPostAPI

And finally, the application can reference the function through service "posts_api":

$postsAPIService = ContainerBuilderFactory::getInstance()->get('posts_api');
$posts = $postsAPIService->getPosts($args);

Function names

If you have noticed from the code demonstrated above, function get_posts is abstracted as getPosts. There are a couple of reasons why this is a good idea:

  • By calling the function differently, it helps identify which code belongs to WordPress and which code belongs to our abstracted application.
  • Function names must be camelCased to comply with PSR-2, which attempts to define a standard for writing PHP code.

Certain functions can be redefined, making more sense in an abstract context. For instance, WordPress function get_user_by($field, $value) uses parameter $field with values "id", "ID", "slug", "email" or "login" to know how to get the user. Instead of replicating this methodology, we can explicitly define a separate function for each of them:

interface UsersAPIInterface
{ public function getUserById($value); public function getUserByEmail($value); public function getUserBySlug($value); public function getUserByLogin($value);
}

And these are resolved for WordPress:

class WPUsersAPI implements UsersAPIInterface
{ public function getUserById($value) { return get_user_by('id', $value); } public function getUserByEmail($value) { return get_user_by('email', $value); } public function getUserBySlug($value) { return get_user_by('slug', $value); } public function getUserByLogin($value) { return get_user_by('login', $value); }
}

Certain other functions should be renamed because their names convey information about their implementation, which may not apply for a different CMS. For instance, WordPress function get_the_author_meta can receive parameter "user_lastname", indicating that the user’s lastname is stored as a “meta” value (which is defined as an additional property for an object, not originally mapped in the database model). However, other CMSs may have a column "lastname" in the user table, so it doesn’t apply as a meta value. (The actual definition of “meta” value is actually inconsistent in WordPress: function get_the_author_meta also accepts value "user_email", even though the email is stored on the user table. Hence, I’d rather stick to my definition of “meta” value, and remove all inconsistencies from the abstracted code.)

Then, our contract will implement the following functions:

interface UsersAPIInterface
{ public function getUserDisplayName($user_id); public function getUserEmail($user_id); public function getUserFirstname($user_id); public function getUserLastname($user_id); ...
}

Which are resolved for WordPress:

class WPUsersAPI implements UsersAPIInterface
{ public function getUserDisplayName($user_id) { return get_the_author_meta('display_name', $user_id); } public function getUserEmail($user_id) { return get_the_author_meta('user_email', $user_id); } public function getUserFirstname($user_id) { return get_the_author_meta('user_firstname', $user_id); } public function getUserLastname($user_id) { return get_the_author_meta('user_lastname', $user_id); } ...
}

Our functions could also be re-defined as to remove the limitations from WordPress. For instance, function update_user_meta($user_id, $meta_key, $meta_value) can receive one meta attribute at a time, which makes sense since each of these is updated on its own database query. However, October CMS maps all meta attributes together on a single database column, so it makes more sense to update all values together on a single database operation. Then, our contract can include an operation updateUserMetaAttributes($user_id, $meta) which can update several meta values at the same time:

interface UserMetaInterface
{ public function updateUserMetaAttributes($user_id, $meta);
}

Which is resolved for WordPress like this:

class WPUsersAPI implements UsersAPIInterface
{ public function updateUserMetaAttributes($user_id, $meta) { foreach ($meta as $meta_key => $meta_value) { update_user_meta($user_id, $meta_key, $meta_value); } }
}

Finally, we may want to re-define a function to remove its ambiguities. For instance, WordPress function add_query_arg can receive parameters in two different ways:

  1. Using a single key and value: add_query_arg('key', 'value', 'http://example.com');
  2. Using an associative array: add_query_arg(['key1' => 'value1', 'key2' => 'value2'], 'http://example.com');

This becomes difficult to keep consistent across CMSs. Hence, our contract can define functions addQueryArg (singular) and addQueryArgs (plural) as to remove the ambiguity:

public function addQueryArg(string $key, string $value, string $url);
public function addQueryArgs(array $key_values, string $url);

Function parameters

We must also abstract the parameters to the function, making sure they make sense in a generic context. For each function to abstract, we must consider:

  • renaming and/or re-defining the parameters;
  • renaming and/or re-defining the attributes passed on array parameters.

For instance, WordPress function get_posts receives a unique parameter $args, which is an array of attributes. One of its attributes is fields which, when given the value "ids", makes the function return an array of IDs instead of an array of objects. However, I deem this implementation too specific for WordPress, and for a generic context I’d prefer a different solution: Convey this information through a separate parameter called $options, under attribute "return-type".

To accomplish this, we add parameter $options to the function in our contract:

interface PostAPIInterface
{ public function getPosts($args, $options = []);
}

Instead of referencing WordPress constant value "ids" (which we can’t guarantee will be the one used in all other CMSs), we create a corresponding constant value for our abstracted application:

class Constants
{ const RETURNTYPE_IDS = 'ids';
}

The WordPress implementation must map and recreate the parameters between the contract and the implementation:

class WPPostAPI implements PostAPIInterface
{ public function getPosts($args, $options = []) { if ($options['return-type'] == Constants::RETURNTYPE_IDS) { $args['fields'] = 'ids'; } return get_posts($args); }
}

And finally, we can execute the code through our contract:

$options = [ 'return-type' => Constants::RETURNTYPE_IDS,
];
$post_ids = $postsAPIService->getPosts($args, $options);

While abstracting the parameters, we should avoid transferring WordPress’s technical debt to our abstracted code, whenever possible. For instance, parameter $args from function get_posts can contain attribute 'post_type'. This attribute name is somewhat misleading, since it can receive one element (post_type => "post") but also a list of them (post_type => "post, event"), so this name should be in plural instead: post_types. When abstracting this piece of code, we can set our interface to expect attribute post_types instead, which will be mapped to WordPress’s post_type.

Similarly, different functions accept arguments with different names, even though these have the same objective, so their names can be unified. For instance, through parameter $args, WordPress function get_posts accepts attribute posts_per_page, and function get_users accepts attribute number. These attribute names can perfectly be replaced with the more generic attribute name limit.

It is also a good idea to rename parameters to make it easy to understand which ones belong to WordPress and which ones have been abstracted. For instance, we can decide to replace all "_" with "-", so our newly-defined argument post_types becomes post-types.

Applying these prior considerations, our abstracted code will look like this:

class WPPostAPI implements PostAPIInterface
{ public function getPosts($args, $options = []) { ... if (isset($args['post-types'])) { $args['post_type'] = $args['post-types']; unset($args['post-types']); } if (isset($args['limit'])) { $args['posts_per_page'] = $args['limit']; unset($args['limit']); } return get_posts($args); }
}

We can also re-define attributes to modify the shape of their values. For instance, WordPress parameter $args in function get_posts can receive attribute date_query, whose properties ("after", "inclusive", etc) can be considered specific to WordPress:

$date = current_time('timestamp');
$args['date_query'] = array( array( 'after' => date('Y-m-d H:i:s', $date), 'inclusive' => true, )
);

To unify the shape of this value into something more generic, we can re-implement it using other arguments, such as "date-from" and "date-from-inclusive" (this solution is not 100% convincing though, since it is more verbose than WordPress’s):

class WPPostAPI implements PostAPIInterface
{ public function getPosts($args, $options = []) { ... if (isset($args['date-from'])) { $args['date_args'][] = [ 'after' => $args['date-from'], 'inclusive' => false, ]; unset($args['date-from']); } if (isset($args['date-from-inclusive'])) { $args['date_args'][] = [ 'after' => $args['date-from-inclusive'], 'inclusive' => true, ]; unset($args['date-from-inclusive']); } return get_posts($args); }
}

In addition, we need to consider if to abstract or not those parameters which are too specific to WordPress. For instance, function get_posts allows to order posts by attribute menu_order, which I don’t think it works in a generic context. Then, I’d rather not abstract this code and keep it on the CMS-specific package for WordPress.

Finally, we can also add argument types (and, since here we are, also return types) to our contract fuction, making it more understandable and allowing the code to fail in compilation time instead of during runtime:

interface PostAPIInterface
{ public function getPosts(array $args, array $options = []): array;
}

States (and other constant values)

We need to make sure that all states have the same meaning in all CMSs. For instance, posts in WordPress can have one among the following states: "publish", "pending", "draft" or "trash". To make sure that the application references the abstracted version of the states and not the CMS-specific one, we can simply define a constant value for each of them:

class PostStates { const PUBLISHED = 'published'; const PENDING = 'pending'; const DRAFT = 'draft'; const TRASH = 'trash';
}

As it can be seen, the actual constant values may or may not be the same as in WordPress: while "publish" was renamed as "published", the other ones remain the same.

For the implementation for WordPress, we convert from the agnostic value to the WordPress-specific one:

class WPPostAPI implements PostAPIInterface
{ public function getPosts($args, $options = []) { ... if (isset($args['post-status'])) { $conversion = [ PostStates::PUBLISHED => 'publish', PostStates::PENDING => 'pending', PostStates::DRAFT => 'draft', PostStates::TRASH => 'trash', ]; $args['post_status'] = $conversion[$args['post-status']]; unset($args['post-status']); } return get_posts($args); }
}

Finally, we can reference these constants throughout our CMS-agnostic application:

$args = [ 'post-status' => PostStates::PUBLISHED,
];
$posts = $postsAPIService->getPosts($args);

This strategy works under the assumption that all CMSs will support these states. If any CMS does not support a particular state (eg: "pending") then it should throw an exception whenever a corresponding functionality is invoked.

CMS helper functions

WordPress implements several helper functions that must also abstracted, such as make_clickable. Because these functions are very generic, we can implement a default behavior for them that works well in an abstract context, and which can be overridden if the CMS implements a better solution.

We first define the contract:

interface HelperAPIInterface
{ public function makeClickable(string $text);
}

And provide a default behaviour for the helper functions through an abstract class:

abstract class AbstractHelperAPI implements HelperAPIInterface
{ public function makeClickable(string $text) { return preg_replace('!(((f|ht)tp(s)?://)[-a-zA-Zа-яА-Я()0-9@:%_+.~#?&;//=]+)!i', '<a href="$1">$1</a>', $text); }
}

Now, our application can either use this functionality or, if it runs on WordPress, use the WordPress-specific implementation:

class WPHelperAPI extends AbstractHelperAPI
{ public function makeClickable(string $text) { return make_clickable($text); }
}

User permissions

For all CMSs which support user management, in addition to abstracting the corresponding functions (such as current_user_can and user_can in WordPress), we must also make sure that the user permissions (or capabilities) have the same effect across all CMSs. To achieve this, our abstracted application needs to explicitly state what is expected from the capability, and the implementation for each CMS must either satisfy it through one of its own capabilities or throw an exception if it can’t satisfy it. For instance, if the application needs to validate if the user can edit posts, it can represent it through a capability called "capability:editPosts", which is satisfied for WordPress through its capability "edit_posts".

This is still an instance of the “code against interfaces, not implementations” principle, however here we run against a problem: Whereas in PHP we can define interfaces and classes to model contracts and service providers (which works in compilation time, so that the code doesn’t compile if a class implementing an interface does not implement all functions defined in the interface), PHP offers no similar construct to validate that a contract capability (which is simply a string, such as "capability:editPosts") has been satisfied through a capability by the CMS. This concept, which I call a “loose contract”, will need to be handled by our application, on runtime.

To deal with “loose contracts”, I have created a service LooseContractService through which:

  • the application can define what “contract names” must be implemented, through function requireNames.
  • the CMS-specific implementations can satisfy those names, through function implementNames.
  • the application can get the implementation of a name through function getImplementedName.
  • the application can also inquire for all non-satisfied required names through function getNotImplementedRequiredNames, as to throw an exception or log the error if needed.

The service looks like this:

class LooseContractService
{ protected $requiredNames = []; protected $nameImplementations = []; public function requireNames(array $names): void { $this->requiredNames = array_merge( $this->requiredNames, $names ); } public function implementNames(array $nameImplementations): void { $this->nameImplementations = array_merge( $this->nameImplementations, $nameImplementations ); } public function getImplementedName(string $name): ?string { return $this->nameImplementations[$name]; } public function getNotImplementedRequiredNames(): array { return array_diff( $this->requiredNames, array_keys($this->nameImplementations) ); }
}

The application, when initialized, can then establish loose contracts by requiring names:

$looseContractService = ContainerBuilderFactory::getInstance()->get('loose_contracts');
$looseContractService->requireNames([ 'capability:editPosts',
]);

And the CMS-specific implementation can satisfy these:

$looseContractService->implementNames([ 'capability:editPosts' => 'edit_posts',
]);

The application can then resolve the required name to the implementation from the CMS. If this required name (in this case, a capability) is not implemented, then the application may throw an exception:

$cmsCapabilityName = $looseContractService->getImplementedName('capability:editPosts');
if (!$cmsCapabilityName) { throw new Exception(sprintf( "The CMS has no support for capability \"%s\"", 'capability:editPosts' ));
}
// Now can use the capability to check for permissions
$userManagementAPIService = ContainerBuilderFactory::getInstance()->get('user_management_api');
if ($userManagementAPIService->userCan($user_id, $cmsCapabilityName)) { ...
}

Alternatively, the application can also fail when first initialized if any one required name is not satisfied:

if ($notImplementedNames = $looseContractService->getNotImplementedRequiredNames()) { throw new Exception(sprintf( "The CMS has not implemented loose contract names %s", implode(', ', $notImplementedNames) ));
}

Application options

WordPress ships with several application options, such as those stored in table wp_options under entries "blogname", "blogdescription", "admin_email", "date_format" and many others. Abstracting application options involves:

  • abstraction the function getOption;
  • abstracting each of the required options, aiming to make the CMS satisfy the notion of this option (eg: if a CMS doesn’t have an option for the site’s description, it can’t return the site’s name instead).

Let’s solve these 2 actions in turn. Concerning function getOption, I believe that we can expect all CMSs to support storing and retrieving options, so we can place the corresponding function under a CMSCoreInterface contract:

interface CMSCoreInterface
{ public function getOption($option, $default = false);
}

As it can be observed from the function signature above, I’m making the assumption that each option will also have a default value. However, I don’t know if every CMS allows setting default values for options. But it doesn’t matter since the implementation can simply return NULL then.

This function is resolved for WordPress like this:

class WPCMSCore implements CMSCoreInterface
{ public function getOption($option, $default = false) { return get_option($option, $default); }
}

To solve the 2nd action, which is abstracting each needed option, it is important to notice that even though we can always expect the CMS to support getOption, we can’t expect it to implement each single option used by WordPress, such as "use_smiles" or "default_ping_status". Hence, we must first filter all options, and abstract only those that make sense in a generic context, such as "siteName" or "dateFormat".

Then, having the list of options to abstract, we can use a “loose contract” (as explained earlier on) and require a corresponding option name for each, such as "option:siteName" (resolved for WordPress as "blogname") or "option:dateFormat" (resolved as "date_format").

Database column names

In WordPress, when we are requesting data from function get_posts we can set attribute "orderby" in $args to order the results, which can be based on a column from the posts table (such as values "ID", "title", "date", "comment_count", etc), a meta value (through values "meta_value" and "meta_value_num") or other values (such as "post__in" and "rand").

Whenever the value corresponds to the table column name, we can abstract them using a “loose contract”, as explained earlier on. Then, the application can reference a loose contract name:

$args = [ 'orderby' => $looseContractService->getImplementedName('dbcolumn:orderby:posts:date'),
];
$posts = $postsAPIService->getPosts($args);

And this name is resolved for WordPress:

$looseContractService->implementNames([ 'dbcolumn:orderby:posts:date' => 'date',
]);

Now, let’s say that in our WordPress application we have created a meta value "likes_count" (which stores how many likes a post has) to order posts by popularity, and we want to abstract this functionality too. To order results by some meta property, WordPress expects an additional attribute "meta_key", like this:

$args = [ 'orderby' => 'meta_value', 'meta_key' => 'likes_count',
];

Because of this additional attribute, I consider this implementation WordPress-specific and very difficult to abstract to make it work everywhere. Then, instead of generalizing this functionality, I can simply expect every CMS to add their own, specific implementation.

Let’s do that. First, I create a helper class to retrieve the CMS-agnostic query:

class QueryHelper
{ public function getOrderByQuery() { return array( 'orderby' => $looseContractService->getImplementedName('dbcolumn:orderby:posts:likesCount'), ); }
}

The OctoberCMS-specific package can add a column "likes_count" to the posts table, and resolve name "dbcolumn:orderby:posts:likesCount" to "like_count" and it will work. The WordPress-specific package, though, must resolve "dbcolumn:orderby:posts:likesCount" as "meta_value" and then override the helper function to add the additional property "meta_key":

class WPQueryHelper extends QueryHelper
{ public function getOrderByQuery() { $query = parent::getOrderByQuery(); $query['meta_key'] = 'likes_count'; return $query; }
}

Finally, we set-up the helper query class as a service in the ContainerBuilder, configure it to be resolved to the WordPress-specific class, and we obtain the query for ordering results:

$queryHelperService = ContainerBuilderFactory::getInstance()->get('query_helper');
$args = $queryHelperService->getOrderByQuery();
$posts = $postsAPIService->getPosts($args);

Abstracting the values for ordering results that do not correspond to column names or meta properties (such as "post__in" and "rand") seems to be more difficult. Because my application doesn’t use them, I haven’t considered how to do it, or even if it is possible. Then I took the easy way out: I have considered these to be WordPress-specific, hence the application makes them available only when running on WordPress.

Errors

When dealing with errors, we must consider abstracting the following elements:

  • the definition of an error;
  • error codes and messages.

Let’s review these in turn.

Definition of an error:

An Error is a special object, different than an Exception, used to indicate that some operation has failed, and why it failed. WordPress represents errors through class WP_Error, and allows to check if some returned value is an error through function is_wp_error.

We can abstract checking for an error:

interface CMSCoreInterface
{ public function isError($object);
}

Which is resolved for WordPress like this:

class WPCMSCore implements CMSCoreInterface
{ public function isError($object) { return is_wp_error($object); }
}

However, to deal with errors in our abstracted code, we can’t expect all CMSs to have an error class with the same properties and methods as WordPress’s WP_Error class. Hence, we must abstract this class too, and convert from the CMS error to the abstracted error after executing a function from the CMS.

The abstract error class Error is simply a slightly modified version from WordPress’s WP_Error class:

class Error { protected $errors = array(); protected $error_data = array(); public function __construct($code = null, $message = null, $data = null) { if ($code) { $this->errors[$code][] = $message; if ($data) { $this->error_data[$code] = $data; } } } public function getErrorCodes() { return array_keys($this->errors); } public function getErrorCode() { if ($codes = $this->getErrorCodes()) { return $codes[0]; } return null; } public function getErrorMessages($code = null) { if ($code) { return $this->errors[$code] ?? []; } // Return all messages if no code specified. return array_reduce($this->errors, 'array_merge', array()); } public function getErrorMessage($code = null) { if (!$code) { $code = $this->getErrorCode(); } $messages = $this->getErrorMessages($code); return $messages[0] ?? ''; } public function getErrorData($code = null) { if (!$code) { $code = $this->getErrorCode(); } return $this->error_data[$code]; } public function add($code, $message, $data = null) { $this->errors[$code][] = $message; if ($data) { $this->error_data[$code] = $data; } } public function addData($data, $code = null) { if (!$code) { $code = $this->getErrorCode(); } $this->error_data[$code] = $data; } public function remove($code) { unset($this->errors[$code]); unset($this->error_data[$code]); }
}

We implement a function to convert from the CMS to the abstract error through a helper class:

class WPHelpers
{ public static function returnResultOrConvertError($result) { if (is_wp_error($result)) { // Create a new instance of the abstracted error class $error = new Error(); foreach ($result->get_error_codes() as $code) { $error->add($code, $result->get_error_message($code), $result->get_error_data($code)); } return $error; } return $result; }
}

And we finally invoke this method for all functions that may return an error:

class UserManagementService implements UserManagementInterface
{ public function getPasswordResetKey($user_id) { $result = get_password_reset_key($user_id); return WPHelpers::returnResultOrConvertError($result); }
}
Error codes and messages:

Every CMS will have its own set of error codes and corresponding explanatory messages. For instance, WordPress function get_password_reset_key can fail due to the following reasons, as represented by their error codes and messages:

  1. "no_password_reset": Password reset is not allowed for this user.
  2. "no_password_key_update": Could not save password reset key to database.

In order to unify errors so that an error code and message is consistent across CMSs, we will need to inspect these and replace them with our custom ones (possibly in function returnResultOrConvertError explained above).

Hooks

Abstracting hooks involves:

  • the hook functionality;
  • the hooks themselves.

Let’s analyze these in turn.

Abstracting the hook functionality

WordPress offers the concept of “hooks”: a mechanism through which we can change a default behavior or value (through “filters”) and execute related functionality (through “actions”). Both Symfony and Laravel offer mechanisms somewhat related to hooks: Symfony provides an event dispatcher component, and Laravel’s mechanism is called events; these 2 mechanisms are similar, sending notifications of events that have already taken place, to be processed by the application through listeners.

When comparing these 3 mechanisms (hooks, event dispatcher and events) we find that WordPress’s solution is the simpler one to set-up and use: Whereas WordPress hooks enable to pass an unlimited number of parameters in the hook itself and to directly modify a value as a response from a filter, Symfony’s component requires to instantiate a new object to pass additional information, and Laravel’s solution suggests to run a command in Artisan (Laravel’s CLI) to generate the files containing the event and listener objects. If all we desire is to modify some value in the application, executing a hook such as $value = apply_filters("modifyValue", $value, $post_id); is as simple as it can get.

In the first part of this series, I explained that the CMS-agnostic application already establishes a particular solution for dependency injection instead of relying on the solution by the CMS, because the application itself needs this functionality to glue its parts together. Something similar happens with hooks: they are such a powerful concept that the application can greatly benefit by making it available to the different CMS-agnostic packages (allowing them to interact with each other) and not leave this wiring-up to be implemented only at the CMS level. Hence, I have decided to already ship a solution for the “hook” concept in the CMS-agnostic application, and this solution is the one implemented by WordPress.

In order to decouple the CMS-agnostic hooks from those from WordPress, once again we must “code against interfaces, not implementations”: We define a contract with the corresponding hook functions:

interface HooksAPIInterface
{ public function addFilter(string $tag, $function_to_add, int $priority = 10, int $accepted_args = 1): void; public function removeFilter(string $tag, $function_to_remove, int $priority = 10): bool; public function applyFilters(string $tag, $value, ...$args); public function addAction(string $tag, $function_to_add, int $priority = 10, int $accepted_args = 1): void; public function removeAction(string $tag, $function_to_remove, int $priority = 10): bool; public function doAction(string $tag, ...$args): void;
}

Please notice that functions applyFilters and doAction are variadic, i.e. they can receive a variable amount of arguments through parameter ...$args. By combining this feature (which was added to PHP in version 5.6, hence it was unavailable to WordPress until very recently) with argument unpacking, i.e. passing a variable amount of parameters ...$args to a function, we can easily provide the implementation for WordPress:

class WPHooksAPI implements HooksAPIInterface
{ public function addFilter(string $tag, $function_to_add, int $priority = 10, int $accepted_args = 1): void { add_filter($tag, $function_to_add, $priority, $accepted_args); } public function removeFilter(string $tag, $function_to_remove, int $priority = 10): bool { return remove_filter($tag, $function_to_remove, $priority); } public function applyFilters(string $tag, $value, ...$args) { return apply_filters($tag, $value, ...$args); } public function addAction(string $tag, $function_to_add, int $priority = 10, int $accepted_args = 1): void { add_action($tag, $function_to_add, $priority, $accepted_args); } public function removeAction(string $tag, $function_to_remove, int $priority = 10): bool { return remove_action($tag, $function_to_remove, $priority); } public function doAction(string $tag, ...$args): void { do_action($tag, ...$args); }
}

As for an application running on Symfony or Laravel, this contract can be satisfied by installing a CMS-agnostic package implementing WordPress-like hooks, such as this one, this one or this one.

Finally, whenever we need to execute a hook, we do it through the corresponding service:

$hooksAPIService = ContainerBuilderFactory::getInstance()->get('hooks_api');
$title = $hooksAPIService->applyFilters("modifyTitle", $title, $post_id);
Abstracting the hooks themselves

We need to make sure that, whenever a hook is executed, a consistent action will be executed no matter which is the CMS. For hooks defined inside of our application that is no problem, since we can resolve them ourselves, most likely in our CMS-agnostic package. However, when the hook is provided by the CMS, such as action "init" (triggered when the system has been initialized) or filter "the_title" (triggered to modify a post’s title) in WordPress, and we invoke these hooks, we must make sure that all other CMSs will process them correctly and consistently. (Please notice that this concerns hooks that make sense in every CMS, such as "init"; certain other hooks can be considered too specific to WordPress, such as filter "rest_{$this->post_type}_query" from a REST controller, so we don’t need to abstract them.)

The solution I found is to hook into actions or filters defined exclusively in the application (i.e. not in the CMS), and to bridge from CMS hooks to application hooks whenever needed. For instance, instead of adding an action for hook "init" (as defined in WordPress), any code in our application must add an action on hook "cms:init", and then we implement the bridge in the WordPress-specific package from "init" to "cms:init":

$hooksAPIService->addAction('init', function() use($hooksAPIService) { $hooksAPIService->doAction('cms:init');
});

Finally, the application can add a “loose contract” name for "cms:init", and the CMS-specific package must implement it (as demonstrated earlier on).

Routing

Different frameworks will provide different solutions for routing (i.e. the mechanism of identifying how the requested URL will be handled by the application), which reflect the architecture of the framework:

  • In WordPress, URLs map to database queries, not to routes.
  • Symfony provides a Routing component which is independent (any PHP application can install it and use it), and which enables to define custom routes and which controller will process them.
  • Laravel’s routing builds on top of Symfony’s routing component to adapt it to the Laravel framework.

As it can be seen, WordPress’s solution is the outlier here: the concept of mapping URLs to database queries is tightly coupled to WordPress’s architecture, and we would not want to restrict our abstracted application to this methodology (for instance, October CMS can be set-up as a flat-file CMS, in which case it doesn’t use a database). Instead, it makes more sense to use Symfony’s approach as its default behavior, and allow WordPress to override this behavior with its own routing mechanism.

(Indeed, while WordPress’s approach works well for retrieving content, it is rather inappropriate when we need to access some functionality, such as displaying a contact form. In this case, before the launch of Gutenberg, we were forced to create a page and add a shortcode "

" to it as content, which is not as clean as simply mapping the route to its corresponding controller directly.)

Hence, the routing for our abstracted application will not be based around the modeled entities (post, page, category, tag, author) but purely on custom-defined routes. This should already work perfectly for Symfony and Laravel, using their own solutions, and there is not much for us to do other than injecting the routes with the corresponding controllers into the application’s configuration.

To make it work in WordPress, though, we need to take some extra steps: We must introduce an external library to handle routing, such as Cortex. Making use of Cortex, the application running on WordPress can have it both ways:

  • if there is a custom-defined route matching the requested URL, use its corresponding controller.
  • if not, let WordPress handle the request in its own way (i.e. retrieving the matched database entity or returning a 404 if no match is successful).

To implement this functionality, I have designed the contract CMSRoutingInterface to, given the requested URL, calculate two pieces of information:

  • the actual route, such as contact, posts or posts/my-first-post.
  • the nature of the route: core nature values "standard", "home" and "404", and additional nature values added through packages such as "post" through a “Posts” package or "user" through a “Users” package.

The nature of the route is an artificial construction that enables the CMS-agnostic application to identify if the route has extra qualities attached to it. For instance, when requesting the URL for a single post in WordPress, the corresponding database object post is loaded into the global state, under global $post. It also helps identify which case we want to handle, to avoid inconsistencies. For instance, we could have defined a custom route contact handled by a controller, which will have nature "standard", and also a page in WordPress with slug "contact", which will have nature "page" (added through a package called “Pages”). Then, our application can prioritize which way to handle the request, either through the controller or through a database query.

Let’s implement it. We first define the service’s contract:

interface CMSRoutingInterface
{ public function getNature(); public function getRoute();
}

We can then define an abstract class which provides a base implementation of these functions:

abstract class AbstractCMSRouting implements CMSRoutingInterface
{ const NATURE_STANDARD = 'standard'; const NATURE_HOME = 'home'; const NATURE_404 = '404'; public function getNature() { return self::NATURE_STANDARD; } public function getRoute() { // By default, the URI path is already the route (minus parameters and trailing slashes) $route = $_SERVER['REQUEST_URI']; $params_pos = strpos($route, '?'); if ($params_pos !== false) { $route = substr($route, 0, $params_pos); } return trim($route, '/'); }
}

And the implementation is overriden for WordPress:

class WPCMSRouting extends AbstractCMSRouting
{ const ROUTE_QUERY = [ 'custom_route_key' => 'custom_route_value', ]; private $query; private function init() { if (is_null($this->query)) { global $wp_query; $this->query = $wp_query; } } private function isStandardRoute() { return !empty(array_intersect($this->query->query_vars, self::ROUTE_QUERY)); } public function getNature() { $this->init(); if ($this->isStandardRoute()) { return self::NATURE_STANDARD; } elseif ($this->query->is_home() || $this->query->is_front_page()) { return self::NATURE_HOME; } elseif ($this->query->is_404()) { return self::NATURE_404; } // Allow components to implement their own natures $hooksAPIService = ContainerBuilderFactory::getInstance()->get('hooks_api'); return $hooksAPIService->applyFilters( "nature", parent::getNature(), $this->query ); }
}

In the code above, please notice how constant ROUTE_QUERY is used by the service to know if the route is a custom-defined one, as configured through Cortex:

$hooksAPIService->addAction( 'cortex.routes', function(RouteCollectionInterface $routes) { // Hook into filter "routes" to provide custom-defined routes $appRoutes = $hooksAPIService->applyFilters("routes", []); foreach ($appRoutes as $route) { $routes->addRoute(new QueryRoute( $route, function (array $matches) { return WPCMSRouting::ROUTE_QUERY; } )); } }
);

Finally, we add our routes through hook "routes":

$hooksAPIService->addFilter( 'routes', function($routes) { return array_merge( $routes, [ 'contact', 'posts', ] ); }
);

Now, the application can find out the route and its nature, and proceed accordingly (for instance, for a "standard" nature invoke its controller, or for a "post" nature invoke WordPress’s templating system):

$cmsRoutingService = ContainerBuilderFactory::getInstance()->get('routing');
$nature = $cmsRoutingService->getNature();
$route = $cmsRoutingService->getRoute();
// Process the requested route, as appropriate
// ...

Object properties

A rather inconvenient consequence of abstracting our code is that we can’t reference the properties from an object directly, and we must do it through a function instead. This is because different CMSs will represent the same object as containing different properties, and it is easier to abstract a function to access the object properties than to abstract the object itself (in which case, among other disadvantages, we may have to reproduce the object caching mechanism from the CMS). For instance, a post object $post contains its ID under $post->ID in WordPress and under $post->id in October CMS. To resolve this property, our contract PostObjectPropertyResolverInterface will contain function getId:

interface PostObjectPropertyResolverInterface { public function getId($post);
}

Which is resolved for WordPress like this:

class WPPostObjectPropertyResolver implements PostObjectPropertyResolverInterface { public function getId($post) { return $post->ID; }
}

Similarly, the post content property is $post->post_content in WordPress and $post->content in October CMS. Our contract will then allow to access this property through function getContent:

interface PostObjectPropertyResolverInterface { public function getContent($post);
}

Which is resolved for WordPress like this:

class WPPostObjectPropertyResolver implements PostObjectPropertyResolverInterface { public function getContent($post) { return $post->post_content; }
}

Please notice that function getContent receives the object itself through parameter $post. This is because we are assuming the content will be a property of the post object in all CMSs. However, we should be cautious on making this assumption, and decide on a property by property basis. If we don’t want to make the previous assumption, then it makes more sense for function getContent to receive the post’s ID instead:

interface PostObjectPropertyResolverInterface { public function getContent($post_id);
}

Being more conservative, the latter function signature makes the code potentially more reusable, however it is also less efficient, because the implementation will still need to retrieve the post object:

class WPPostObjectPropertyResolver implements PostObjectPropertyResolverInterface { public function getContent($post_id) { $post = get_post($post_id); return $post->post_content; }
}

In addition, some properties may be needed in their original value and also after applying some processing; for these cases, we will need to implement a corresponding extra function in our contract. For instance, the post content needs be accessed also as HTML, which is done through executing apply_filters('the_content', $post->post_content) in WordPress, or directly through property $post->content_html in October CMS. Hence, our contract may have 2 functions to resolve the content property:

interface PostObjectPropertyResolverInterface { public function getContent($post_id); // = raw content public function getHTMLContent($post_id);
}

We must also be concerned with abstracting the value that the property can have. For instance, a comment is approved in WordPress if its property comment_approved has the value "1". However, other CMSs may have a similar property with value true. Hence, the contract should remove any potential inconsistency or ambiguity:

interface CommentObjectPropertyResolverInterface { public function isApproved($comment);
}

Which is implemented for WordPress like this:

class WPCommentObjectPropertyResolver implements CommentObjectPropertyResolverInterface { public function isApproved($comment) { return $comment->comment_approved == "1"; }
}

Global state

WordPress sets several variables in the global context, such as global $post when querying a single post. Keeping variables in the global context is considered an anti-pattern, since the developer could unintentionally override their values, producing bugs that are difficult to track down. Hence, abstracting our code gives us the chance to implement a better solution.

An approach we can take is to create a corresponding class AppState which simply contains a property to store all variables that our application will need. In addition to initializing all core variables, we enable components to initialize their own ones through hooks:

class AppState
{ public static $vars = []; public static function getVars() { return self::$vars; } public static function initialize() { // Initialize core variables self::$vars['nature'] = $cmsRoutingService->getNature(); self::$vars['route'] = $cmsRoutingService->getRoute(); // Initialize $vars through hooks self::$vars = $hooksAPIService->applyFilters("AppState:init", self::$vars); return self::$vars; }
}

To replace global $post, a hook from WordPress can then set this data through a hook. A first step would be to set the data under "post-id":

$hooksAPIService->addFilter( "AppState:init", function($vars) { if (is_single()) { global $post; $vars['post-id'] => $post->ID; } return $vars; }
);

However, we can also abstract the global variables: instead of dealing with fixed entities (such as posts, users, comments, etc), we can deal with the entity in a generic way through "object-id", and we obtain its properties by inquiring the nature of the requested route:

$hooksAPIService->addFilter( "AppState:init", function($vars) { if ($vars['nature'] == 'post') { global $post; $vars['object-id'] => $post->ID; } return $vars; }
);

From now own, if we need to display a property of the current post, we access it from the newly defined class instead of the global context:

$vars = AppState::getVars();
$object_id = $vars['object-id'];
// Do something with it
// ...

Entity models (meta, post types, pages being posts, and taxonomies —tags and categories—)

We must abstract those decisions made for WordPress concerning how its entities are modeled. Whenever we consider that WordPress’s opinionatedness makes sense in a generic context too, we can then replicate such a decision for our CMS-agnostic code.

Meta:

As mentioned earlier, the concept of “meta” must be decoupled from the model entity (such as “post meta” from “posts”), so if a CMS doesn’t provide support for meta, it can then discard only this functionality.

Then, package “Post Meta” (decoupled from, but dependent on, package “Posts”) defines the following contract:

interface PostMetaAPIInterface
{ public function getMetaKey($meta_key); public function getPostMeta($post_id, $key, $single = false); public function deletePostMeta($post_id, $meta_key, $meta_value = ''); public function addPostMeta($post_id, $meta_key, $meta_value, $unique = false); public function updatePostMeta($post_id, $meta_key, $meta_value);
}

Which is resolved for WordPress like this:

class WPPostMetaAPI implements PostMetaAPIInterface
{ public function getMetaKey($meta_key) { return '_'.$meta_key; } public function getPostMeta($post_id, $key, $single = false) { return get_post_meta($post_id, $key, $single); } public function deletePostMeta($post_id, $meta_key, $meta_value = '') { return delete_post_meta($post_id, $meta_key, $meta_value); } public function addPostMeta($post_id, $meta_key, $meta_value, $unique = false) { return add_post_meta($post_id, $meta_key, $meta_value, $unique); } public function updatePostMeta($post_id, $meta_key, $meta_value) { return update_post_meta($post_id, $meta_key, $meta_value); }
}
Post types:

I have decided that WordPress’s concept of a custom post type, which allows to model entities (such as an event or a portfolio) as extensions of posts, can apply in a generic context, and as such, I have replicated this functionality in the CMS-agnostic code. This decision is controversial, however, I justify it because the application may need to display a feed of entries of different types (such as posts, events, etc) and custom post types make such implementation feasible. Without custom post types, I would expect the application to need to execute several queries to bring the data for every entity type, and the logic would get all muddled up (for instance, if fetching 12 entries, should we fetch 6 posts and 6 events? but what if the events were posted much earlier than the last 12 posts? and so on).

What happens when the CMS doesn’t support this concept? Well, nothing serious happens: a post will still indicate its custom post type to be a “post”, and no other entities will inherit from the post. The application will still work properly, just with some slight overhead from the unneeded code. This is a trade-off that, I believe, is more than worth it.

To support custom post types, we simply add a function getPostType in our contract:

interface PostAPIInterface
{ public function getPostType($post_id);
}

Which is resolved for WordPress like this:

class WPPostAPI implements PostAPIInterface
{ public function getPostType($post_id) { return get_post_type($post_id); }
}
Pages being posts:

While I justify keeping custom post types in order to extend posts, I don’t justify a page being a post, as it happens in WordPress, because in other CMSs these entities are completely decoupled and, more importantly, a page may have higher rank than a post, so making a page extend from a post would make no sense. For instance, October CMS ships pages in its core functionality, but posts must be installed through plugins.

Hence we must create separate contracts for posts and pages, even though they may contain the same functions:

interface PostAPIInterface
{ public function getTitle($post_id);
} interface PageAPIInterface
{ public function getTitle($page_id);
}

To resolve these contracts for WordPress and avoid duplicating code, we can implement the common functionality through a trait:

trait WPCommonPostFunctions
{ public function getTitle($post_id) { return get_the_title($post_id); }
} class WPPostAPI implements PostAPIInterface
{ use WPCommonPostFunctions;
} class WPPageAPI implements PageAPIInterface
{ use WPCommonPostFunctions;
}
Taxonomies (tags and categories):

Once again, we can’t expect all CMSs to support what is called taxonomies in WordPress: tags and categories. Hence, we must implement this functionality through a package “Taxonomies”, and, assuming that tags and categories are added to posts, make this package dependent on package “Posts”.

interface TaxonomyAPIInterface
{ public function getPostCategories($post_id, $options = []); public function getPostTags($post_id, $options = []); public function getCategories($query, $options = []); public function getTags($query, $options = []); public function getCategory($cat_id); public function getTag($tag_id); ...
}

We could have decided to create two separate packages “Categories” and “Tags” instead of “Taxonomies”, however, as the implementation in WordPress makes evident, a tag and a category are basically the same concept of entity with only a tiny difference: categories are hierarchical (i.e. a category can have a parent category), but tags are not. Then, I consider that it makes sense to keep this concept for a generic context, and shipped under a single package “Taxonomies”.

We must pay attention that certain functionalities involve both posts and taxonomies, and these must be appropriately decoupled. For instance, in WordPress we can retrieve posts that were tagged "politics" by executing get_posts(['tag' => "politics"]). In this case, while function getPosts must be implemented in package “Posts”, filtering by tags must be implemented in package “Taxonomies”. To accomplish this separation, we can simply execute a hook in the implementation of function getPosts for WordPress, allowing any component to modify the arguments before executing get_posts:

class WPPostAPI implements PostAPIInterface
{ public function getPosts($args) { $args = $hooksAPIService->applyFilters("modifyArgs", $args); return get_posts($args); }
}

And finally we implement the hook in package “Taxonomies for WordPress”:

$hooksAPIService->addFilter( 'modifyArgs', function($args) { if (isset($args['tags'])) { $args['tag'] = implode(',', $args['tags']); unset($args['tags']); } if (isset($args['categories'])) { $args['cat'] = implode(',', $args['categories']); unset($args['categories']); } return $args; }
);

Please notice that in the abstracted code the attributes were re-defined (following the recommendations for abstracting function parameters, explained earlier on): "tag" must be provided as "tags" and "cat" must be provided as "categories" (shifting the connotation from singular to plural), and these values must be passed as arrays (i.e. removed accepting comma-separated strings as in WordPress, to add consistency).

Translation

Because calls to translate strings are spread all over the application code, translation is not a functionality that we can opt out from, and we should make sure that the other frameworks are compatible with our chosen translation mechanism.

In WordPress, which implements internationalization through gettext, we are required to set-up translation files for each locale code (such as ‘fr_FR’, which is the code for french language from FRance), and these can be set under a text domain (which allows themes or plugins to define their own translations without fear of collision with the translations from other pieces of code). We don’t need to check for support for placeholders in the string to translate (such as when doing sprintf(__("Welcome %s"), $user_name)), because function sprintf belongs to PHP and not to the CMS, so it will always work.

Let’s check if the other frameworks support the required two properties, i.e. getting the translation data for a specific locale composed of language and country, and under a specific text domain:

  • Symfony’s translation component supports these two properties.
  • The locale used in Laravel’s localization involves the language but not the country, and text domains are not supported (they could be replicated through overriding package language files, but the domain is not explicitly set, so the contract and the implementation would be inconsistent with each other).

However, luckily there is library Laravel Gettext which can replace Laravel’s native implementation with Symfony’s translation component. Hence, we got support for all frameworks, and we can rely on a WordPress-like solution.

We can then define our contract mirroring the WordPress function signatures:

interface TranslationAPIInterface
{ public function __($text, $domain = 'default'); public function _e($text, $domain = 'default');
}

The implementation of the contract for WordPress is like this:

class WPTranslationAPI implements TranslationAPIInterface
{ public function __($text, $domain = 'default') { return __($text, $domain); } public function _e($text, $domain = 'default') { _e($text, $domain); }
}

And to use it in our application, we do:

$translationAPI = ContainerBuilderFactory::getInstance()->get('translation_api');
$text = $translationAPI->__("translate this", "my-domain");

Media

WordPress has media management as part of its core functionality, which represents a media element as an entity all by itself, and allows to manipulate the media element (such as cropping or resizing images), but we can’t expect all CMSs to have similar functionality. Hence, media management must be decoupled from the CMS core functionality.

For the corresponding contract, we can mirror the WordPress media functions, but removing WordPress’s opinionatedness. For instance, in WordPress, a media element is a post (with post type "attachment"), but for the CMS-agnostic code it is not, hence the parameter must be $media_id (or $image_id) instead of $post_id. Similarly, WordPress treats media as attachments to posts, but this doesn’t need to be the case everywhere, hence we can remove the word “attachment” from the function signatures. Finally, we can decide to keep the $size of the image in the contract; if the CMS doesn’t support creating multiple image sizes for an image, then it can just fall back on its default value NULL and nothing grave happens:

interface MediaAPIInterface
{ public function getImageSrcAndDimensions($image_id, $size = null): array; public function getImageURL($image_id, $size = null): string;
}

The response by function getImageSrcAndDimensions can be asbtracted too, returning an array of our own design instead of simply re-using the one from the WordPress function wp_get_attachment_image_src:

class WPMediaAPI implements MediaAPIInterface
{ public function getImageSrcAndDimensions($image_id, $size = null): array { $img_data = wp_get_attachment_image_src($image_id, $size); return [ 'src' => $img_data[0], 'width' => $img_data[1], 'height' => $img_data[2], ]; } public function getImageURL($image_id, $size = null): string { return wp_get_attachment_image_url($image_id, $size); }
}

Conclusion

Setting-up a CMS-agnostic architecture for our application can be a painful endeavor. As it was demonstrated in this article, abstracting all the code was a lengthy process, taking plenty of time and energy to achieve, and it is not even finished yet. I wouldn’t be surprised if the reader is intimidated by the idea of going through this process in order to convert a WordPress application into a CMS-agnostic one. If I hadn’t done the abstraction myself, I would certainly be intimidated too.

My suggestion is for the reader is to analyze if going through this process makes sense based on a project-by-project basis. If there is no need whatsoever to port an application to a different CMS, then you will be right to stay away from this process and stick to the WordPress way. However, if you do need to migrate an application away from WordPress and want to reduce the effort required, or if you already need to maintain several codebases which would benefit from code reusability, or even if you may migrate the application sometime in the future and you have just started a new project, then this process is for you. It may be painful to implement, but well worth it. I know because I’ve been there. But I’ve survived, and I’d certainly do it again. Thanks for reading.

Smashing Editorial (dm, yk, il)
The Power (and Fun) of Scope with CSS Custom Properties

You’re probably already at least a little familiar with CSS variables. If not, here’s a two-second overview: they are really called custom properties, you set them in declaration blocks like --size: 1em and use them as values like font-size: var(--size);, they differ from preprocessor variables (e.g. they cascade), and here’s a guide with way more information.

But are we using them to their full potential? Do we fall into old habits and overlook opportunities where variables could significantly reduce the amount of code we write?

This article was prompted by a recent tweet I made about using CSS variables to create dynamic animation behavior.

https://platform.twitter.com/widgets.js

Let’s look at a couple of instances where CSS variables can be used to do some pretty cool things that we may not have considered.

Basic scoping wins

The simplest and likely most common example would be scoped colors. And what’s our favorite component to use with color? The button. 😅

Consider the standard setup of primary and secondary buttons. Let’s start with some basic markup that uses a BEM syntax.

<button class="button button--primary">Primary</button>
<button class="button button--secondary">Secondary</button>

Traditionally, we might do something like this to style them up:

.button { padding: 1rem 1.25rem; color: #fff; font-weight: bold; font-size: 1.25rem; margin: 4px; transition: background 0.1s ease;
} .button--primary { background: hsl(233, 100%, 50%); outline-color: hsl(233, 100%, 80%);
} .button--primary:hover { background: hsl(233, 100%, 40%);
} .button--primary:active { background: hsl(233, 100%, 30%);
} .button--secondary { background: hsl(200, 100%, 50%); outline-color: hsl(200, 100%, 80%);
} .button--secondary:hover { background: hsl(200, 100%, 40%);
} .button--secondary:active { background: hsl(200, 100%, 30%);
}

See the Pen
Basic buttons
by Jhey (@jh3y)
on CodePen.

That’s an awful lot of code for something not particularly complex. We haven’t added many styles and we’ve added a lot of rules to cater to the button’s different states and colors. We could significantly reduce the code with a scoped variable.

In our example, the only differing value between the two button variants is the hue. Let’s refactor that code a little then. We won’t change the markup but cleaning up the styles a little, we get this:

.button { padding: 1rem 1.25rem; color: #fff; font-weight: bold; font-size: 1.25rem; margin: 1rem; transition: background 0.1s ease; background: hsl(var(--hue), 100%, 50%); outline-color: hsl(var(--hue), 100%, 80%); }
.button:hover { background: hsl(var(--hue), 100%, 40%);
} .button:active { background: hsl(var(--hue), 100%, 30%);
} .button--primary { --hue: 233;
} .button--secondary { --hue: 200;
}

See the Pen
Refactoring styles with a scoped variable
by Jhey (@jh3y)
on CodePen.

This not only reduces the code but makes maintenance so much easier. Change the core button styles in one place and it will update all the variants! 🙌

I’d likely leave it there to make it easier for devs wanting to use those buttons. But, we could take it further. We could inline the variable on the actual element and remove the class declarations completely. 😲

<button class="button" style="--hue: 233;">Primary</button>
<button class="button" style="--hue: 200;">Secondary</button>

Now we don’t need these. 👍

.button--primary { --hue: 233;
} .button--secondary { --hue: 200;
}

See the Pen
Scoping w/ inline CSS variables
by Jhey (@jh3y)
on CodePen.

Inlining those variables might not be best for your next design system or app but it does open up opportunities. Like, for example, if we had a button instance where we needed to override the color.

button.button.button--primary(style=`--hue: 20;`) Overridden

See the Pen
Overridden with inline scope
by Jhey (@jh3y)
on CodePen.

Having fun with inline variables

Another opportunity is to have a little fun with it. This is a technique I use for many of the Pens I create over on CodePen. 😉

You may be writing straightforward HTML, but in many cases, you may be using a framework, like React or a preprocessor like Pug, to write your markup. These solutions allow you to leverage JavaScript to create random inline variables. For the following examples, I’ll be using Pug. Pug is an indentation-based HTML templating engine. If you aren’t familiar with Pug, do not fear! I’ll try to keep the markup simple.

Let’s start by randomizing the hue for our buttons:

button.button(style=`--hue: ${Math.random() * 360}`) First

With Pug, we can use ES6 template literals to inline randomized CSS variables. 💪

See the Pen
Random inline CSS variable hues
by Jhey (@jh3y)
on CodePen.

Animation alterations

So, now that we have the opportunity to define random characteristics for an element, what else could we do? Well, one overlooked opportunity is animation. True, we can’t animate the variable itself, like this:

@keyframes grow { from { --scale: 1; } to { --scale: 2; }
}

But we can create dynamic animations based on scoped variables. We can change the behavior of animation on the fly! 🤩

Example 1: The excited button

Let’s create a button that floats along minding its own business and then gets excited when we hover over it.

Start with the markup:

button.button(style=`--hue: ${Math.random() * 360}`) Show me attention

A simple floating animation may look like this:

@keyframes flow { 0%, 100% { transform: translate(0, 0); } 50% { transform: translate(0, -25%); }
}

This will give us something like this:

See the Pen
The excited button foundation
by Jhey (@jh3y)
on CodePen.

I’ve added a little shadow as an extra but it’s not vital. 👍

Let’s make it so that our button gets excited when we hover over it. Now, we could simply change the animation being used to something like this:

.button:hover { animation: shake .1s infinite ease-in-out;
} @keyframes shake { 0%, 100% { transform: translate(0, 0) rotate(0deg); } 25% { transform: translate(-1%, 3%) rotate(-2deg); } 50% { transform: translate(1%, 2%) rotate(2deg); } 75% { transform: translate(1%, -2%) rotate(-1deg); }
}

And it works:

See the Pen
The excited button gets another keyframes definition
by Jhey (@jh3y)
on CodePen.

But, we need to introduce another keyframes definition. What if we could merge the two animations into one? They aren’t too far off from each other in terms of structure.

We could try:

@keyframes flow-and-shake { 0%, 100% { transform: translate(0, 0) rotate(0deg); } 25%, 75% { transform: translate(0, -12.5%) rotate(0deg); } 50% { transform: translate(0, -25%) rotate(0deg); }
}

Although this works, we end up with an animation that isn’t quite as smooth because of the translation steps. So what else could we do? Let’s find a compromise by removing the steps at 25% and 75%.

@keyframes flow-and-shake { 0%, 100% { transform: translate(0, 0) rotate(0deg); } 50% { transform: translate(0, -25%) rotate(0deg); }
}

It works fine, as we expected, but here comes the trick: Let’s update our button with some variables.

.button { --y: -25; --x: 0; --rotation: 0; --speed: 2;
}

Now let’s plug them into the animation definition, along with the button’s animation properties.

.button { animation-name: flow-and-shake; animation-duration: calc(var(--speed) * 1s); animation-iteration-count: infinite; animation-timing-function: ease-in-out;
} @keyframes flow-and-shake { 0%, 100% { transform: translate(calc(var(--x) * -1%), calc(var(--y) * -1%)) rotate(calc(var(--rotation) * -1deg)); } 50% { transform: translate(calc(var(--x) * 1%), calc(var(--y) * 1%)) rotate(calc(var(--rotation) * 1deg)); }
}

All is well. 👍

Let’s change those values when the button is hovered:

.button:hover { --speed: .1; --x: 1; --y: -1; --rotation: -1;
}

See the Pen
The excited button with refactored keyframes & scoped variables
by Jhey (@jh3y)
on CodePen.

Nice! Now our button has two different types of animations but defined via one set of keyframes. 🤯

Let’s have a little more fun with it. If we take it a little further, we can make the button a little more playful and maybe stop animating altogether when it’s active. 😅

See the Pen
The Excited Button w/ dynamic animation 🤓
by Jhey (@jh3y)
on CodePen.

Example 2: Bubbles

Now that we’ve gone through some different techniques for things we can do with the power of scope, let’s put it all together. We are going to create a randomly generated bubble scene that heavily leverages scoped CSS variables.

Let’s start by creating a bubble. A static bubble.

.bubble { background: radial-gradient(100% 115% at 25% 25%, #fff, transparent 33%), radial-gradient(15% 15% at 75% 75%, #80dfff, transparent), radial-gradient(100% 100% at 50% 25%, transparent, #66d9ff 98%); border: 1px solid #b3ecff; border-radius: 100%; height: 50px; width: 50px;
}

We are using background with multiple values and a border to make the bubble effect — but it’s not very dynamic. We know the border-radius will always be the same. And we know the structure of the border and background will not change. But the values used within those properties and the other property values could all be random.

Let’s refactor the CSS to make use of variables:

.bubble { --size: 50; --hue: 195; --bubble-outline: hsl(var(--hue), 100%, 50%); --bubble-spot: hsl(var(--hue), 100%, 75%); --bubble-shade: hsl(var(--hue), 100%, 70%); background: radial-gradient(100% 115% at 25% 25%, #fff, transparent 33%), radial-gradient(15% 15% at 75% 75%, var(--bubble-spot), transparent), radial-gradient(100% 100% at 50% 25%, transparent, var(--bubble-shade) 98%); border: 1px solid var(--bubble-outline); border-radius: 100%; height: calc(var(--size) * 1px); width: calc(var(--size) * 1px);
}

That’s a good start. 👍

See the Pen
Bubbles foundation
by Jhey (@jh3y)
on CodePen.

Let’s add some more bubbles and leverage the inline scope to position them as well as size them. Since we are going to start randomizing more than one value, it’s handy to have a function to generate a random number in range for our markup.

- const randomInRange = (max, min) => Math.floor(Math.random() * (max - min + 1)) + min

With Pug, we can utilize iteration to create a large set of bubbles:

- const baseHue = randomInRange(0, 360)
- const bubbleCount = 50
- let b = 0
while b < bubbleCount - const size = randomInRange(10, 50) - const x = randomInRange(0, 100) .bubble(style=`--x: ${x}; --size: ${size}; --hue: ${baseHue}`) - b++

Updating our .bubble styling allows us to make use of the new inline variables.

.bubble { left: calc(var(--x) * 1%); position: absolute; transform: translate(-50%, 0);
}

Giving us a random set of bubbles:

See the Pen
Adding bubbles
by Jhey (@jh3y)
on CodePen.

Let’s take it even further and animate those bubbles so they float from top to bottom and fade out.

.bubble { animation: float 5s infinite ease-in-out; top: 100%;
} @keyframes float { from { opacity: 1; transform: translate(0, 0) scale(0); } to { opacity: 0; transform: translate(0, -100vh) scale(1); }
}

See the Pen
Bubbles rising together
by Jhey (@jh3y)
on CodePen.

That’s pretty boring. They all do the same thing at the same time. So let’s randomize the speed, delay, end scale and distance each bubble is going to travel.

- const randomInRange = (max, min) => Math.floor(Math.random() * (max - min + 1)) + min
- const baseHue = randomInRange(0, 360)
- const bubbleCount = 50
- let b = 0
while b < bubbleCount - const size = randomInRange(10, 50) - const delay = randomInRange(1, 10) - const speed = randomInRange(2, 20) - const distance = randomInRange(25, 150) - const scale = randomInRange(100, 150) / 100 - const x = randomInRange(0, 100) .bubble(style=`--x: ${x}; --size: ${size}; --hue: ${baseHue}; --distance: ${distance}; --speed: ${speed}; --delay: ${delay}; --scale: ${scale}`) - b++

And now, let’s update our styles

.bubble { animation-name: float; animation-duration: calc(var(--speed) * 1s); animation-delay: calc(var(--delay) * -1s); animation-iteration-count: infinite; animation-timing-function: ease-in-out;
} @keyframes float { from { opacity: 1; transform: translate(-50%, 0) scale(0); } to { opacity: 0; transform: translate(-50%, calc(var(--distance) * -1vh)) scale(var(--scale)); }
}

And we will get this:

See the Pen
Random bubble scene using variable scope 😎
by Jhey (@jh3y)
on CodePen.

With around 50 lines of code, you can create a randomly generated animated scene by honing the power of the scope! 💪

That’s it!

We can create some pretty cool things with very little code by putting CSS variables to use and leveraging some little tricks.

I do hope this article has raised some awareness for the power of CSS variable scope and I do hope you will hone the power and pass it on 😎

All the demos in this article are available in this CodePen collection.

The post The Power (and Fun) of Scope with CSS Custom Properties appeared first on CSS-Tricks.

iOS 13 Broke the Classic Pure CSS Parallax Technique

I know. You hate parallax. You know what we should hate more? When things that used to work on the web stop working without any clear warning or idea why.

Way back in 2014, Keith Clark blogged an exceptionally clever CSS trick where you essentially use a CSS transform to scale an element down affecting how it appears to scroll, then “depth correcting” it back to “normal” size. Looks like we got five years of fun out of that, but it’s stopped working in iOS 13.

Here’s a video of official simulators and the problem:

I’d like to raise my hand for un-breaking this. If we don’t watchdog for the web, everything will suffer.

The post iOS 13 Broke the Classic Pure CSS Parallax Technique appeared first on CSS-Tricks.

Mastering OOP: A Practical Guide To Inheritance, Interfaces, And Abstract Classes

Mastering OOP: A Practical Guide To Inheritance, Interfaces, And Abstract Classes

Mastering OOP: A Practical Guide To Inheritance, Interfaces, And Abstract Classes

Ryan Kay

So far as I can tell, it is uncommon to come across educational content in the field of software development which provides an appropriate mixture of theoretical and practical information. If I was to guess why, I assume it is because individuals who focus on theory tend to get into teaching, and individuals who focus on practical information tend to get paid to solve specific problems, using specific languages and tools.

This is, of course, a broad generalization, but if we accept it briefly for arguments sake, it follows that many people (by no means all people) who take on the role of teacher, tend to be either poor at, or utterly incapable of explaining the practical knowledge relevant to a particular concept.

In this article, I will do my best to discuss three core mechanisms which you will find in most Object Oriented Programming (OOP) languages: Inheritance, interfaces (a.k.a. protocols), and abstract classes. Rather than giving you technical and complex verbal explanations of what each mechanism is, I will do my best to focus on what they do, and when to use them.

However, before addressing them individually, I would like to briefly discuss what it means to give a theoretically sound, yet practically useless explanation. My hope is that you can use this information to help you sift through different educational resources and to avoid blaming yourself when things do not make sense.

Different Degrees Of Knowing

Knowing Names

Knowing the name of something is arguably the most shallow form of knowing. In fact, a name is only generally useful to the extent that it either is commonly used by many people to refer to the same thing and/or it helps to describe the thing. Unfortunately, as anyone who has spent time in this field has discovered, many people use different names for the same thing (e.g. interfaces and protocols), the same names for different things (e.g. modules and components), or names which are esoteric to the point of being absurd (e.g. Either Monad). Ultimately, names are just pointers (or references) to mental models, and they can be of varying degrees of usefulness.

To make this field even more difficult to study, I would hazard a guess that for most individuals, writing code is (or at least was) a very unique experience. Even more complicated is understanding how that code is ultimately compiled into machine language, and represented in physical reality as a series of electrical impulses changing over time. Even if one can recall the names of the processes, concepts, and mechanisms that are employed in a program, there is no guarantee that the mental models which one creates for such things are consistent with the models another individual; let alone whether they are objectively accurate.

It is for these reasons, alongside the fact that I do not have a naturally good memory for jargon, that I consider names to be the least important aspect of knowing something. That is not to say that names are useless, but I have in the past learned and employed many design patterns in my projects, only to learn of the commonly used name months, or even years later.

Knowing Verbal Definitions And Analogies

Verbal definitions are the natural starting point for describing a new concept. However, as with names, they can be of varying degrees of usefulness and relevance; much of that depending on what the end goals of the learner are. The most common problem I see in verbal definitions is assumed knowledge typically in the form of jargon.

Suppose for example, that I was to explain that a thread is very much like a process, except that threads occupy the same address space of a given process. To someone who is already familiar with processes and address spaces, I have essentially stated that threads can be associated with their understanding of a process (i.e. they possess many of the same characteristics), but they can be differentiated based on a distinct characteristic.

To someone who does not possess that knowledge, I have at best not made any sense, and at worst caused the learner to feel inadequate in some way for not knowing the things I have assumed they should know. In fairness, this is acceptable if your learners really ought to possess such knowledge (such as teaching graduate students or experienced developers), but I consider it to be a monumental failure to do so in any introductory level material.

Often it is very difficult to provide a good verbal definition of a concept when it is unlike anything else the learner has seen before. In this case, it is very important for the teacher to select an analogy which is likely to be familiar to the average person, and also relevant insofar as it conveys many of the same qualities of the concept.

For example, it is critically important for a software developer to understand what it means when software entities (different parts of a program) are tightly-coupled or loosely-coupled. When building a garden shed, a junior carpenter may think that it is faster and easier to put it together using nails instead of screws. This is true up until the point at which a mistake is made, or a change in the design of the garden shed necessitates rebuilding part of the shed.

At this point, the decision to use nails to tightly-couple the parts of the garden shed together, has made the construction process as a whole more difficult, likely slower, and extracting nails with a hammer runs the risk of damaging the structure. Conversely, screws can take a bit of extra time to assemble, but they are easy to remove and pose little risk of damaging nearby parts of the shed. This is what I mean by loosely-coupled. Naturally, there are cases where you really just need a nail, but that decision ought to be guided by critical thinking and experience.

As I will discuss in detail later on, there are different mechanisms for connecting parts of a program together which provide varying degrees of coupling; just like nails and screws. While my analogy may have helped you understand what this critically important term means, I have not given you any idea of how to apply it outside of the context of building a garden shed. This leads me to the most important kind of knowing, and the key to deeply understanding vague and difficult concepts in any field of inquiry; although we will stick to writing code in this article.

Knowing In Code

In my opinion, strictly regarding software development, the most import form of knowing a concept comes from being able to use it in the working application code. This form of knowing can be attained simply by writing lots of code and solving many different problems; jargon names and verbal definitions need not be included.

In my own experience, I recall solving the problem of communicating with a remote database, and a local database through a single interface (you will know what that means soon if you do not already); rather than the client (whatever class which talks to the interface) needing to call the remote and local (or even a test database) explicitly. In fact, the client had no idea what was behind the interface, so I did not need to change it regardless of if it was running in a production app or a test environment. About a year after I solved this problem, I came across the term “Facade Pattern”, and not long after the term “Repository Pattern”, which are both names that people use for the solution described prior.

All of this preamble is to hopefully illuminate some of the flaws which are most often made in explaining topics such as inheritance, interfaces, and abstract classes. Of the three, inheritance is likely the simplest one to both use and understand. In my experience both as a student of programming, and a teacher, the other two are almost invariably an issue to learners unless very special attention is paid to avoiding the mistakes discussed earlier. From this point on, I will do my best to make these topics as simple as they should be, but no simpler.

A Note On The Examples

Being most fluent in Android mobile application development myself, I will use examples taken from that platform so that I may teach you about building GUI applications at the same time as introducing language features of Java. However, I will not be going into so much detail that the examples ought to be unintelligible by someone with a cursory understanding of Java EE, Swing or JavaFX. My ultimate goal in discussing these topics is to help you to understand what they mean in the context of solving a problem in just about any sort of application.

I would also like to warn you, dear reader, that at times it may seem like I am being needlessly philosophical and pedantic about specific words and their definitions. The reason for this is that there truly is a deep philosophical underpinning required to understand the difference between something which is concrete (real), and something which is abstract (less detailed than a real thing). This understanding applies to many things outside of the field of computing, but it is of particularly high importance for any software developer to grasp the nature of abstractions. In any case, if my words fail you, the examples in code will hopefully not.

Inheritance And Implementation

When it comes to building applications with a graphical user interface (GUI), inheritance is arguably the most important mechanism for making it possible to quickly build an application.

Although there is a lesser understood benefit to using inheritance to be discussed later, the primary benefit is to share implementation between classes. This word “implementation”, at least for the purposes of this article, has a distinct meaning. To give a general definition of the word in English, I might say that to implement something, is to make it real.

To give a technical definition specific to software development, I might say that to implement a piece of software, is to write concrete lines of code which satisfy the requirements of said piece of software. For example, suppose I am writing a sum method:
private double sum(double first, double second){

private double sum(double first, double second){ //TODO: implement
}

The snippet above, even though I have made it as far as writing a return type (double) and a method declaration which specifies the arguments (first, second) and the name which can be used to call said method (sum), it has not been implemented. In order to implement it, we must complete the method body like so:

private double sum(double first, double second){ return first + second;
}

Naturally, the first example would not compile, but we will see momentarily that interfaces are a way in which we can write these sorts of unimplemented functions without errors.

Inheritance In Java

Presumably, if you are reading this article, you have used the extends Java keyword at least once. The mechanics of this keyword are simple and most often described using examples to do with different kinds of animals or geometric shapes; Dogand Catextend Animal, and so forth. I will assume that I do not need to explain rudimentary type theory to you, so let us get right into the primary benefit of inheritance in Java, via the extendskeyword.

Building a console-based “Hello World” application in Java is very simple. Assuming you possess a Java Compiler (javac) and runtime environment (jre), you can write a class which contains a main function like so:

public class JavaApp{ public static void main(String []args){ System.out.println("Hello World"); }
}

Building a GUI application in Java on almost any of its main platforms (Android, Enterprise/Web, Desktop), with a bit of help from an IDE to generate the skeleton/boilerplate code of a new app, is also relatively easy thanks to the extendskeyword.

Suppose that we have an XML Layout called activity_main.xml (we typically build user interfaces declaratively in Android, via Layout files) containing a TextView(like a text label) called tvDisplay:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/tvDisplay" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" /> </FrameLayout>

Also, suppose that we would like tvDisplayto say “Hello World!” To do so, we simply need to write a class which uses the extendskeyword to inherit from the Activityclass:

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ((TextView)findViewById(R.id.tvDisplay)).setText("Hello World"); }

The effect of inheriting the implementation of the Activityclass can be best appreciated by taking a quick look at its source code. I highly doubt that Android would have become the dominant mobile platform if one needed to implement even a small portion of the 8000+ lines necessary to interact with the system, just to generate a simple window with some text. Inheritance is what allows us to not have to rebuild the Android framework, or whatever platform you happen to be working with, from scratch.

Inheritance Can Be Used For Abstraction

Insofar as it can be used to share implementation across classes, inheritance is relatively simple to understand. However, there is another important way in which inheritance can be used, which is conceptually related to the interfaces and abstract classes which we will be discussing soon.

If you please, suppose for the next little while that an abstraction, used in the most general sense, is a less detailed representation of a thing. Instead of qualifying that with a lengthy philosophical definition, I will try to point out how abstractions work in daily life, and shortly thereafter discuss them expressly in terms of software development.

Suppose you are traveling to Australia, and you are aware that the region you are visiting is host to a particularly high density of inland taipan snakes (they are apparently quite poisonous). You decide to consult Wikipedia to learn more about them by looking at images and other information. By doing so, you are now acutely aware of a particular kind of snake which you have never seen before.

Abstractions, ideas, models, or whatever else you want to call them, are less detailed representations of a thing. It is important that they are less detailed than the real thing because a real snake can bite you; images on Wikipedia pages typically do not. Abstractions are also important because both computers and human brains have a limited capacity to store, communicate, and process information. Having enough detail to use this information in a practical way, without taking up too much space in memory, is what makes it possible for computers and human brains alike to solve problems.

To tie this back into inheritance, all of the three main topics I am discussing here can be used as abstractions, or mechanisms of abstraction. Suppose that in our “Hello World” app’s layout file, we decide to add an ImageView, Button, and ImageButton:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/btnDisplay" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <ImageButton android:id="@+id/imbDisplay" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <ImageView android:id="@+id/imvDisplay" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</LinearLayout>

Also suppose that our Activity has implemented View.OnClickListener to handle clicks:

public class MainActivity extends Activity implements View.OnClickListener { private Button b; private ImageButton ib; private ImageView iv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //... b = findViewById(R.id.imvDisplay).setOnClickListener(this); ib = findViewById(R.id.btnDisplay).setOnClickListener(this); iv = findViewById(R.id.imbDisplay).setOnClickListener(this); } @Override public void onClick(View view) { final int id = view.getId(); //handle click based on id... }
}

The key principle here is that Button, ImageButton, and ImageViewinherit from the Viewclass. The result is that this function onClickcan receive click events from disparate (though hierarchically related) UI elements by referencing them as their less detailed parent class. This is far more convenient than having to write a distinct method for handling every kind of widget on the Android platform (not to mention custom widgets).

Interfaces And Abstraction

You may have found the previous code example to be a bit uninspiring, even if you understood why I chose it. Being able to share implementation across a hierarchy of classes is incredibly useful, and I would argue that to be the primary utility of inheritance. As for allowing us to treat a set of classes that have a common parent class as equal in type (i.e. as the parent class), that feature of inheritance has limited use.

By limited, I am speaking of the requirement of child classes to be within the same class hierarchy in order to be referenced via, or known as the parent class. In other words, inheritance is a very restrictive mechanism for abstraction. In fact, if I suppose that abstraction is a spectrum that moves between different levels of detail (or information), I might say that inheritance is the least abstract mechanism for abstraction in Java.

Before I proceed to discussing interfaces, I would like to mention that as of Java 8, two features called Default Methods and Static Methods have been added to interfaces. I will discuss them eventually, but for the moment I would like us to pretend that they do not exist. This is in order for me to make it easier to explain the primary purpose of using an interface, which was initially, and arguably still is, the most abstract mechanism for abstraction in Java.

Less Detail Means More Freedom

In the section on inheritance, I gave a definition of the word implementation, which was meant to contrast with another term we will now go into. To be clear, I do not care about the words themselves, or whether you agree with their usage; only that you understand what they conceptually point to.

Whereas inheritance is primarily a tool to share implementation across a set of classes, we might say that interfaces are primarily a mechanism to share behavior across a set of classes. Behavior used in this sense is truly just a non-technical word for abstract methods. An abstract method is a method which does not, in fact cannot, contain a method body:

public interface OnClickListener { void onClick(View v);
}

The natural reaction for me, and a number of individuals whom I have tutored, after first looking at an interface, was to wonder what the utility of sharing only a return type, method name, and parameter list might be. On the surface, it looks like a great way to create extra work for yourself, or whoever else might be writing the class which implementsthe interface. The answer, is that interfaces are perfect for situations where you want a set of classes to behave in the same manner (i.e. they possess the same public abstract methods), but you expect them to implement that behavior in different ways.

To take a simple but relevant example, the Android platform possesses two classes which are primarily in the business of creating and managing part of the user interface: Activity and Fragment. It follows that these classes will very often have the requirement of listening to events which pop up when a widget is clicked (or otherwise interacted with by a user). For argument’s sake, let us take a moment to appreciate why inheritance will almost never solve such a problem:

public class OnClickManager { public void onClick(View view){ //Wait a minute... Activities and Fragments almost never //handle click events exactly the same way... }
}

Not only would making our Activities and Fragments inherit from OnClickManagermake it impossible to handle events in a different manner, but the kicker is that we could not even do that if we wanted to. Both Activity and Fragment already extend a parent class, and Java does not allow multiple parent classes. So our problem is that we want a set of classes to behave the same way, but we must have flexibility on how the class implements that behavior. This brings us back to the earlier example of the View.OnClickListener:

public interface OnClickListener { void onClick(View v);
}

This is the actual source code (which is nested in the Viewclass), and these few lines allow us to ensure consistent behavior across different widgets (Views) and UI controllers (Activities, Fragments, etc.).

Abstraction Promotes Loose-Coupling

I have hopefully answered the general question about why interfaces exist in Java; among many other languages. From one perspective, they are just a means of sharing code between classes, but they are deliberately less detailed in order to allow for different implementations. But just as inheritance can be used both as a mechanism for sharing code and abstraction (albeit with restrictions on class hierarchy), it follows that interfaces provide a more flexible mechanism for abstraction.

In an earlier section of this article, I introduced the topic of loose/tight-coupling by analogy of the difference between using nails and screws to build some kind of structure. To recap, the basic idea is that you will want to use screws in situations where changing the existing structure (which can be a result of fixing mistakes, design changes, and so forth) is likely to happen. Nails are fine to use when you just need to fasten parts of the structure together and are not particularly worried about taking them apart in the near future.

Nails and screws are meant to be analogous to concrete and abstract references (the term dependencies also applies) between classes. Just so there is no confusion, the following sample will demonstrate what I mean:

class Client { private Validator validator; private INetworkAdapter networkAdapter; void sendNetworkRequest(String input){ if (validator.validateInput(input)) { try { networkAdapter.sendRequest(input); } catch (IOException e){ //handle exception } } }
} class Validator { //...validation logic boolean validateInput(String input){ boolean isValid = true; //...change isValid to false based on validation logic return isValid; }
} interface INetworkAdapter { //... void sendRequest(String input) throws IOException;
}

Here, we have a class called Client which possesses two kinds of references. Notice that, assuming Clientdoes not have anything to do with creating its references (it really should not), it is decoupled from the implementation details of any particular network adapter.

There are a few important implications of this loose coupling. For starters, I can build Clientin absolute isolation of any implementation of INetworkAdapter. Imagine for a moment that you are working in a team of two developers; one to build the front end, one to build the back end. As long as both developers are kept aware of the interfaces which couple their respective classes together, they can carry on with the work virtually independently of one another.

Secondly, what if I were to tell you that both developers could verify that their respective implementations functioned properly, also independently of each other’s progress? This is very easy with interfaces; just build a Test Double which implementsthe appropriate interface:

class FakeNetworkAdapter implements INetworkAdapter { public boolean throwError = false; @Override public void sendRequest(String input) throws IOException { if (throwError) throw new IOException("Test Exception"); }
}

In principle, what can be observed is that working with abstract references opens the door to increased modularity, testability, and some very powerful design patterns such as the Facade Pattern, Observer Pattern, and more. They can also allow developers to find a happy balance of designing different parts of a system based on behavior (Program To An Interface), without getting bogged down in implementation details.

A Final Point On Abstractions

Abstractions do not exist in the same way as a concrete thing. This is reflected in the Java Programming language by the fact that abstract classes and interfaces may not be instantiated.

For example, this would definitely not compile:

public class Main extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { //ERROR x2: Foo f = new Foo(); Bar b = new Bar() } private abstract class Foo{} private interface Bar{} }

In fact, the idea of expecting an unimplemented interface or abstract class to function at runtime makes as much sense as expecting a UPS uniform to float around delivering packages. Something concrete must be behind the abstraction for it to be of utility; even if the calling class does not need to know what is actually behind abstract references.

Abstract Classes: Putting It All Together

If you have made it this far, then I am happy to tell you that I have no more philosophical tangents or jargon phrases to translate. Simply put, abstract classes are a mechanism for sharing implementation and behavior across a set of classes. Now, I will admit straight away that I do not find myself using abstract classes all that often. Even so, my hope is that by the end of this section you will know exactly when they are called for.

Workout Log Case Study

Roughly a year into building Android apps in Java, I was rebuilding my first Android app from scratch. The first version was the kind of horrendous mass of code that you would expect from a self-taught developer with little guidance. By the time I wanted to add new functionality, it became clear that the tightly coupled structure I had built exclusively with nails, was so impossible to maintain that I must rebuild it entirely.

The app was a workout log that was designed to allow easy recording of your workouts, and the ability to output the data of a past workout as a text or image file. Without getting into too much detail, I structured the data models of the app such that there was a Workoutobject, which comprised of a collection of Exerciseobjects (among other fields which are irrelevant to this discussion).

As I was implementing the feature for outputting workout data to some kind of visual medium, I realized that I had to deal with a problem: Different kinds of exercises would require different kinds of text outputs.

To give you a rough idea, I wanted to change the outputs depending on the type of exercise like so:

  • Barbell: 10 REPS @ 100 LBS
  • Dumbbell: 10 REPS @ 50 LBS x2
  • Bodyweight: 10 REPS @ Bodyweight
  • Bodyweight +: 10 REPS @ Bodyweight + 45 LBS
  • Timed: 60 SEC @ 100 LBS

Before I proceed, note that there were other types (working out can become complicated) and that the code I will be showing has been trimmed down and changed to fit nicely into an article.

In keeping with my definition from before, the goal of writing an abstract class, is to implement everything (even state such as variables and constants) which is shared across all child classes in the abstract class. Then, for anything which changes across said child classes, create an abstract method:

abstract class Exercise { private final String type; protected final String name; protected final int[] repetitionsOrTime; protected final double[] weight; protected static final String POUNDS = "LBS"; protected static final String SECONDS = "SEC "; protected static final String REPETITIONS = "REPS "; public Exercise(String type, String name, int[] repetitionsOrTime, double[] weight) { this.type = type; this.name = name; this.repetitionsOrTime = repetitionsOrTime; this.weight = weight; } public String getFormattedOutput(){ StringBuilder sb = new StringBuilder(); sb.append(name); sb.append("\n"); getSetData(sb); sb.append("\n"); return sb.toString(); } /** * Append data appropriately based on Exercise type * @param sb - StringBuilder to Append data to */ protected abstract void getSetData(StringBuilder sb); //...Getters
} 

I may be stating the obvious, but if you have any questions about what should or should not be implemented in the abstract class, the key is to look at any part of the implementation which has been repeated in all child classes.

Now that we have established what is common amongst all exercises, we can begin to create child classes with specializations for each kind of String output:

Barbell Exercise:
class BarbellExercise extends Exercise { public BarbellExercise(String type, String name, int[] repetitionsOrTime, double[] weight) { super(type, name, repetitionsOrTime, weight); } @Override protected void getSetData(StringBuilder sb) { for (int i = 0; i < repetitionsOrTime.length; i++) { sb.append(repetitionsOrTime[i]); sb.append(" "); sb.append(REPETITIONS); sb.append(" @ "); sb.append(weight[i]); sb.append(POUNDS); sb.append("\n"); } }
}
Dumbbell Exercise:
class DumbbellExercise extends Exercise { private static final String TIMES_TWO = "x2"; public DumbbellExercise(String type, String name, int[] repetitionsOrTime, double[] weight) { super(type, name, repetitionsOrTime, weight); } @Override protected void getSetData(StringBuilder sb) { for (int i = 0; i < repetitionsOrTime.length; i++) { sb.append(repetitionsOrTime[i]); sb.append(" "); sb.append(REPETITIONS); sb.append(" @ "); sb.append(weight[i]); sb.append(POUNDS); sb.append(TIMES_TWO); sb.append("\n"); } }
}
Bodyweight Exercise:
class BodyweightExercise extends Exercise { private static final String BODYWEIGHT = "Bodyweight"; public BodyweightExercise(String type, String name, int[] repetitionsOrTime, double[] weight) { super(type, name, repetitionsOrTime, weight); } @Override protected void getSetData(StringBuilder sb) { for (int i = 0; i < repetitionsOrTime.length; i++) { sb.append(repetitionsOrTime[i]); sb.append(" "); sb.append(REPETITIONS); sb.append(" @ "); sb.append(BODYWEIGHT); sb.append("\n"); } }
}

I am certain that some astute readers will find things which could have been abstracted out in a more efficient manner, but the purpose of this example (which has been simplified from the original source) is to demonstrate the general approach. Of course, no programming article would be complete without something which can be executed. There are several online Java compilers which you may use to run this code if you want to test it out (unless you already have an IDE):

public class Main { public static void main(String[] args) { //Note: I actually used another nested class called a "Set" instead of an Array //to represent each Set of an Exercise. int[] reps = {10, 10, 8}; double[] weight = {70.0, 70.0, 70.0}; Exercise e1 = new BarbellExercise( "Barbell", "Barbell Bench Press", reps, weight ); Exercise e2 = new DumbbellExercise( "Dumbbell", "Dumbbell Bench Press", reps, weight ); Exercise e3 = new BodyweightExercise( "Bodyweight", "Push Up", reps, weight ); System.out.println( e1.getFormattedOutput() + e2.getFormattedOutput() + e3.getFormattedOutput() ); }
}

Executing this toy application yields the following output:
Barbell Bench Press

10 REPS @ 70.0LBS
10 REPS @ 70.0LBS
8 REPS @ 70.0LBS Dumbbell Bench Press
10 REPS @ 70.0LBSx2
10 REPS @ 70.0LBSx2
8 REPS @ 70.0LBSx2 Push Up
10 REPS @ Bodyweight
10 REPS @ Bodyweight
8 REPS @ Bodyweight

Further Considerations

Earlier, I mentioned that there are two features of Java interfaces (as of Java 8) which are decidedly geared towards sharing implementation, as opposed to behavior. These features are known as Default Methods and Static Methods.

I have decided not to go into detail on these features for the reason that they are most typically used in mature and/or large code bases where a given interface has many inheritors. Despite the fact that this is meant to be an introductory article, and I still encourage you to take a look at these features eventually, even though I am confident that you will not need to worry about them just yet.

I would also like to mention that there are other ways to share implementation across a set of classes (or even static methods) in a Java application that does not require inheritance or abstraction at all. For example, suppose you have some implementation which you expect to use in a variety of different classes, but does not necessarily make sense to share via inheritance. A common pattern in Java is to write what is known as a Utility class, which is a simple classcontaining the requisite implementation in a static method:

public class TimeConverterUtil { /** * Accepts an hour (0-23) and minute (0-59), then attempts to format them into an appropriate * format such as 12, 30 -> 12:30 pm */ public static String convertTime (int hour, int minute){ String unformattedTime = Integer.toString(hour) + ":" + Integer.toString(minute); DateFormat f1 = new SimpleDateFormat("HH:mm"); Date d = null; try { d = f1.parse(unformattedTime); } catch (ParseException e) { e.printStackTrace(); } DateFormat f2 = new SimpleDateFormat("h:mm a"); return f2.format(d).toLowerCase(); }
}

Using this static method in an external class (or another static method) looks like this:


public class Main { public static void main(String[] args){ //... String time = TimeConverterUtil.convertTime(12, 30); //... }
}

Cheat Sheet

We have covered a lot of ground in this article, so I would like to spend a moment summarizing the three main mechanisms based on what problems they solve. Since you should possess a sufficient understanding of the terms and ideas I have either introduced or redefined for the purposes of this article, I will keep the summaries brief.

I Want A Set Of Child Classes To Share Implementation

Classic inheritance, which requires a child class to inherit from a parent class, is a very simple mechanism for sharing implementation across a set of classes. An easy way to decide if some implementation should be pulled into a parent class, is to see whether it is repeated in a number of different classes line for line. The acronym DRY (Don’t Repeat Yourself) is a good mnemonic device to watch out for this situation.

While coupling child classes together with a common parent class can present some limitations, a side benefit is that they can all be referenced as the parent class, which provides a limited degree of abstraction.

I Want A Set Of Classes To Share Behavior

Sometimes, you want a set of classes to be capable of possessing certain abstract methods (referred to as behavior), but you do not expect the implementation of that behavior to be repeated across inheritors.

By definition, Java interfaces may not contain any implementation (except for Default and Static Methods), but any class which implements an interface, must supply an implementation for all abstract methods, otherwise, the code will not compile. This provides a healthy measure of flexibility and restriction on what is actually shared and does not require the inheritors to be of the same class hierarchy.

I Want A Set Of Child Classes To Share Behavior And Implementation

Although I do not find myself using abstract classes all over the place, they are perfect for situations when you require a mechanism for sharing both behavior and implementation across a set of classes. Anything which will be repeated across inheritors may be implemented directly in the abstract class, and anything which requires flexibility may be specified as an abstract method.

Smashing Editorial (dm, il)
Privacy Settings
We use cookies to enhance your experience while using our website. If you are using our Services via a browser you can restrict, block or remove cookies through your web browser settings. We also use content and scripts from third parties that may use tracking technologies. You can selectively provide your consent below to allow such third party embeds. For complete information about the cookies we use, data we collect and how we process them, please check our Privacy Policy
Youtube
Consent to display content from Youtube
Vimeo
Consent to display content from Vimeo
Google Maps
Consent to display content from Google