2019 in Review
Strategy

Static Hoisting


Since I published my 2019 in Review, detailing the industry trend towards static hosting and the rise of Jamstack CDNs like Vercel, a question has persistently come up: how is this different from a server and a traditional CDN on top? Why not then just use servers with containers (even serverless ones)?

The answer boils down to our ability to not just host static assets and cache them, but also hoist them.

The word hoisting is used quite frequently in JavaScript to describe how the compiler extracts and “moves” declarations to the “top” of a scope, which makes the following code actually work:

function a () {
  return b()  // this works with no errors!
  function b() {
    return "hello world"
  }
}

Calling b() before it was defined works because the declaration was hoisted

The term is also used in compiler optimization lingo to describe a class of optimization where the code is analyzed, and parts that look “static” or invariant are moved (hoisted) outside of loops.

function a (b, c) {
  let sum = []
  for (let i = 0; i < 100000; i++) {
    sum[i] = i + (b + c) // hoist ↗
  }
}
a(314, 159)

Notice that the sum b + c has nothing to do with the context of the loop: it’s loop-invariant. An optimizing compiler can recognize it, and hoist it automatically so that the computation behaves as if you had written it outside of the loop yourself.

Hoisting computation within a program is great, and odds are the compilers and VMs you use every day have plenty of optimizations like it.

What Jamstack as a software architecture has now made possible, however, is to hoisting the results of computation to the edge, right next to where your visitors are.

A core tenet of Jamstack has been to pre-render (pre-compute) as much as possible, which has given prominence to static site generation. The key idea is that computation that would have happened later on, in the request’s timeline, has now been shifted to the build phase, performed once and made available for all users to share.

User RequestEdge ServerStatic HoistingComputation done ahead-of-timeand always shared by all edgesComputation done just-in-timeand partially² shared upon cache HITProxy to a server¹Legacy CDN (JIT)Jamstack (AOT)PerformanceAvailabilityCost✓ Optimal performance✓ Faster cache misses✓ Always online✓ Automatic global failover✓ Optimally inexpensive✓ Zero maintenance overhead⨯ Dependent on DevOps / SRE⨯ Expensive HA (multi-AZ)³⨯ Servers constantly running³⁴⨯ DevOps / Monitoring / SRE⨯ Slower cache misses⨯ Impacted by cold boots³¹ The downsides of this approach apply equally to server-rendering and operating your own static file server² Cache hits will be more rare for less-trafficked pages or sites, and will be highly region-dependent ³ Cold boots can be atenuated by Lambda Provisioning, which drives costs up quite significantly⁴ Functions and serverless containers provide natural multi-az, but are subject to higher costs due to [3]

Since Next.js 9, the next build process has started automatically outputting the optimal asset on a per-page (entrypoint) basis. Futher, with Next.js 9.3 the hooks for static-site generation were refined, and incremental static generation was introduced for appending (and soon updating) pages.

Next.js makes hoisting the static parts of your site or app to the edge a breeze. Let’s look at the build output of a complex site (vercel.com) as an example:

$ next build
  Page                                                           Size     First Load JS
  ┌ ○ /                                                          215 B           191 kB
  ├ ○ /about                                                     4.53 kB         171 kB
  ├ λ /api/sso
  ├ ○ /dashboard                                                 228 B           192 kB
  ├ ○ /bitbucket                                                 11 kB           164 kB
  ├ ● /blog                                                      54.6 kB         224 kB
  ├   └ css/a73431369cd0a9ce449f.css                             960 B
  ├ ● /blog/[post]                                               3.54 kB         206 kB
  ├   ├ /blog/environment-variables-ui
  ├   ├ /blog/simpler-pricing
  ├   ├ /blog/canceling-ongoing-deployments
  └   └ [+95 more paths]
λ  (Lambda)  server-side renders at runtime (uses getInitialProps or getServerSideProps)
○  (Static)  automatically rendered as static HTML (uses no initial props)
●  (SSG)     automatically generated as static HTML + JSON (uses getStaticProps)

The outputs of the Next.js build process vary by the data fetching strategy used by the developer

As you can see from the symbols (○ ● λ) to represent the output types:

  • We build the homepage statically. Our company’s homepage, our “cover letter”, gets built as index.html and pushed to the edge for maximum speed and reliability.
  • The /api functions are exported as serverless (lambda) functions. These are created by placing files inside ./pages/api (more).
  • At build time, we create blog posts by querying an API using Static Site Generation hooks. Blog posts, like our homepage, are thus statically hoisted (optimized) to the edge network.
  • Our dashboard, despite being super dynamic in nature, is a static HTML page that queries data securely using React Hooks from the client side.

CDNs are great and have been around for a long time, and so have static hosts. However, for the longest time, CDNs have been treating the “origin” as an opaque black box.

It’s now possible, instead, to push content directly to the network and design frameworks that optimize for this capability. As a result, with optimizations like static asset hoisting, websites are now becoming faster and more reliable than ever before.

To start developing a Jamstack site or app, check out Next.js which you can deploy to the Vercel edge network with a couple clicks.





Source link

Nullish values in Node 14
Strategy

What’s New With Node? Interview With Bethany Griggs, Node.js…


Node.js 14 is available now. We wanted to get more context and details about the state of Node, and why developers should care about Node.js 14. We talked with Bethany Griggs, Node.js Technical Steering Committee member and Open-source Engineer at IBM, to find out more. 

Bethany has been a Node Core Collaborator for over two years. She contributes to the open-source Node.js runtime and is a member of the Node.js Release Working Group where she is involved with auditing commits for the long-term support (LTS) release lines and the creation of releases. 

Bethany presents and runs workshops at international conferences, including at NodeSummit, London Node.js User Group, and NodeConfEU.

So, you’ve been working as a Node Core collaborator for 2 years, how have things changed? What have you seen from your point of view?

One of the biggest changes is the emphasis on onboarding new contributors to major parts of the project. Getting new names and faces onboarded in a position where they can contribute to Node and also an increase in socializing how people can contribute. 

That means onboarding new feature contributors, contributors to Node.js core, and also onboarding contributors into the specific working groups, such as the Release Working Group and the Package Maintenance Working Group. 

There’s also been several code-and-learn events at various conferences. This is where existing collaborators and contributors mentor attendees through one of their first contributions to Node.js.

Where should people go to get started contributing? 

Go to Node ToDo — it’s a website that walks you through a path towards your first contribution to Node.js. As long as you’re a little bit familiar with Node.js, you can start here. 

The other option is to look for labels in the Node.js GitHub repository tagged with “Good first issue”. 

What are the biggest challenges for Node.js Core this year? 

I think keeping up with the volume of activity in general. We typically have 5-10 pull requests opened on GitHub per day, with most of the new commits landing on master. From master, the commit lands in the current release line. After that, each commit gets audited for suitability for the LTS release lines. So we’re maintaining three different release lines at a time and having a constant flow of incoming pull requests to keep up with.

We’re maintaining the LTS release lines with our enterprise users in mind. The LTS release lines provide them with stability — meaning they’re not having to deal with breaking changes so often. Corporate upgrade schedules are normally more traditional and longer, so we have to provide LTS releases to take into account the steady schedules of large enterprises.

In order to handle this, we currently have 100 active collaborators who can review and land PRs. And we have active mentorship to bring in more contributors and are also actively mentoring new releasers so that they can help out with the auditing. 

The package maintenance team was announced last year – Has Node.js package maintenance improved? 

The package maintenance team is still going through the phase of documenting best practices and getting the opinions of enough module maintainers to create those best practices and those guides. You need to set this groundwork first. We can see some adoption of our best practices, such as testing across Node.js versions. 

I don’t think we’re quite at the point where it has widely improved because we’re still trying to define the best practices. We’re trying to onboard as many maintainers, authors, and consumers as possible when curating these best practices so that we consider as many situations and use-cases as possible.

Do you have an example where the Package Maintenance Working Group has helped?

We used Express.js as one of our pilot projects. And we’ve successfully created a triaging effort via the Package Maintenance Working Group. Express is very popular. They have many, many repositories in Github where people raise issues and the maintainers can struggle at times to keep up with how many issues were raised. 

Some issues are actual bugs reported, some are just, “Hey, I’m not sure how to use Express, or this particular API.” So, via Package Maintenance, we started a triaging effort where people could volunteer to help triage the issues for Express. The Package Maintenance team is trying to define the process and structure of how you can get help triaging your own issues.

The Package Maintenance team is trying to come up with some best practices and processes for maintainers or module authors to follow when they need help, or can no longer dedicate time to maintaining their module.

Members of the Package Maintenance group can get involved with defining the best practice documentation and guides. But it’s also possible to get involved with the triaging efforts via Package Maintenance, so signing up to triage issues for modules such as Express.js.

What’s new in Node.js 14, and why should I upgrade?

One of the main features is the V8 upgrade. Most major releases of Node.js come with a major V8 upgrade. Node.js 14 bumped it to V8 8.1. The main features that come with V8 8.1 include Optional Chaining, which is a relatively recent JavaScript language feature that went through Ecma TC39 in the past few years. 

Long chains of property accesses in JavaScript can be error-prone, as any of them might evaluate to null or undefined. These are known as “nullish” values. Checking for property existence on each step easily turns into a deeply-nested structure of if-statements or a long if-condition replicating the property access chain. Optioning Chaining basically simplifies the code required here and has already been implemented in some other languages.

Nullish values in Node 14

Another nice improvement is the nullish coalescing operator, ??. A new short-circuiting operator meant to handle default values has been added, so that when true or false values in JavaScript aren’t clearly defined. Values for && and || don’t fit in every scenario, and this can lead to hard-to-understand bugs. 

Also, there are some international date-time format additions and more.

One other thing to point out: You should take caution before upgrading your production applications to use Node.js 14. Until Node.js 14 is LTS, there will be slightly more churn and feature additions. I’d personally recommend holding off upgrading to Node.js 14 until it is promoted to LTS in October 2020, particularly for critical production applications. 

However, now is a great time to get ahead and start testing your applications or modules on Node.js 14 and also try out some of the new features. This will test your migration path to Node.js 14, your transition to using Node.js 14 when it is promoted to LTS will go more smoothly if you’ve been already testing with it in advance.



Source link

How to Create Custom WordPress Editor Blocks in 2020
Strategy

How to Create Custom WordPress Editor Blocks in 2020


The WordPress block editor (previously titled Gutenberg) includes a new way to add content to your WordPress posts, pages and soon all content on your WordPress site. It marks WordPress’ move into the page builder space.

The base set of default blocks is pretty robust, but what if you wanted to create your own custom block for laying out content? In the past you would use something like Advanced Custom Fields (ACF) or shortcodes. These days, custom blocks are where it’s at.

I’ve been working with React full-time for the past year, rebuilding WP Migrate DB Pro’s frontend. React is on my mind, and given that the WordPress editor is also written in React, I thought it would be a good idea to see what it would take to create a custom block. So let’s get into it!

Getting started

We’re going to go over what it takes to go from nothing to a relatively basic, custom Gutenberg block. The documentation for the block editor is still kind of all over the place but there is some decent information in the “Block Editor Handbook”. Unfortunately I didn’t find it all that easy to parse, so we’ll go over some of the steps I went through to get set up.

One thing I would recommend before diving head first into creating a Gutenberg block, for now at least, is to get a solid understanding of React and modern JavaScript. If you’re not familiar with things like JSX you could use the ES5 syntax, but you’d be best suited to use the modern syntax in the long run.

It’s fairly straightforward these days to get set up with the WP CLI ‘scaffold’ command. This command will set up a WordPress theme or plugin with a ‘blocks’ folder that contains the PHP and base CSS and JavaScript required to create a custom block. The only drawback that I noticed is that the JavaScript uses the old ES5 syntax rather than modern ESNext. Modern JavaScript allows us to write more concise code and use JSX in our custom block code.

You can also use the ‘create-guten-block’ tool by Ahmad Awais. It gives you a lot of the boilerplate stuff you need out of the box, like Webpack, ESNext support etc. Setting it up is fairly straightforward, and it’s similar to Create React App.

However, rather than using a third-party tool, I used one of the example custom blocks available in the gutenberg-examples repo on Github. The recipe card example covers a lot of what you’ll want in a minimally interactive custom block. It also includes @wordpress/scripts which is a package to run and build our JavaScript code, allowing us to use ‘ESNext’ syntax and JSX.

To get up and running, I just copied the recipe card example locally, modified the fields in the package.json file, and ran yarn.

Blocks

Ok, so what is a ‘Block’ anyway? I had a hard time understanding this concept when I first started working with the block editor.

From the documentation:

Using a system of Blocks to compose and format content, the new block-based editor is designed to create rich, flexible layouts for websites and digital products. Content is created in the unit of blocks instead of freeform text with inserted media, embeds and Shortcodes…

Basically a ‘block’ is an organizational unit for editable ‘stuff’ in WordPress. At least that’s my take on it.

Blocks are the core building ‘block’, but how do you make one? Well, that part I can explain! Blocks are made almost entirely in JavaScript. Gutenberg brings a couple new actions (enqueue_block_assets and enqueue_block_editor_assets) to let you include your JavaScript and stylesheets to be used with Gutenberg.

function fancy_custom_block_block_init() {

    // automatically load dependencies and version
    $asset_file = include( plugin_dir_path( __FILE__ ) . 'build/index.asset.php');

    wp_register_script(
        'fancy-custom-block-block-editor',
        plugins_url( 'build/index.js', __FILE__ ),
        $asset_file['dependencies'],
        $asset_file['version']
    );

    wp_register_style(
        'fancy-custom-block-block-editor',
        plugins_url( 'editor.css', __FILE__ ),
        array( ),
        filemtime( plugin_dir_path( __FILE__ ) . 'editor.css' )
    );

    wp_register_style(
        'fancy-custom-block-block',
        plugins_url( 'style.css', __FILE__ ),
        array( ),
        filemtime( plugin_dir_path( __FILE__ ) . 'style.css' )
    );

    register_block_type( 'fancy-block-plugin/fancy-custom-block', array(
        'editor_script' => 'fancy-custom-block-block-editor',
        'editor_style'  => 'fancy-custom-block-block-editor',
        'style'         => 'fancy-custom-block-block',
    ) );
}

add_action( 'init', 'fancy_custom_block_block_init' );

All you essentially need to do PHP-wise is enqueue your JavaScript and CSS files and call register_block_type() with each asset’s handle.

A world of JavaScript

If you were thinking there’d be more PHP from this point, think again! From here on in, we’re in a JavaScript world.

The key aspect to creating blocks in Gutenberg is the registerBlockType() function. It does all the work.

registerBlockType( 'my-block/cool-block-name', {
    // ... Massive JS object
}

And that’s it! See you next time!

Ok so, there’s a bit more to it than that, but at its core, this is how to create a WordPress Gutenberg block. In the ‘Massive JS object’ there’s a few things to take note of.

If you take a look in /05-recipe-card-esnext/src/index.js file in the gutenberg-examples repo, you can see there’s a lot of config that goes into setting up a block. There are three main sections we’ll go over – attributes and the edit() and save() methods.

Attributes

If you want a block that actually does something, like allow you to edit text, you have to use Gutenberg’s state management system. That’s what the attributes object is. It’s pretty much identical to React’s state management concept, a top level object that keeps track of properties and what’s changed.

attributes: {
    title: {
        type: "array",
        source: "children",
        selector: ".callout-title"
    },
    mediaID: {
        type: "number"
    },
    mediaURL: {
        type: "string",
        source: "attribute",
        selector: "img",
        attribute: "src"
    },
    body: {
        type: "array",
        source: "children",
        selector: ".callout-body"
    },
    alignment: {
        type: "string"
    }
},

The above is a JavaScript object that’s used to configure the appearance of the block.

You can see that for each editable ‘thing’ in your block you need to define an attribute for it. There’s the mediaID and mediaURL for the image, values for a title and body content, as well as the overall alignment of everything. The edit() method takes these attributes as an argument so we can modify them in the editor interface.

edit()

The edit() function lets you customize the editing interface in Gutenberg. If you’re familiar with React’s render() function it’s pretty similar. You essentially specify a return statement that has your JSX in it.

 edit: props => {
    const {
        className,
        isSelected,
        attributes: { mediaID, mediaURL, body, alignment, title },
        setAttributes
    } = props;

    useEffect(() => {
    // console.log(props);
    });

    const onChangeTitle = value => {
        setAttributes({ title: value });
    };

    const onSelectImage = media => {
        setAttributes({
            mediaURL: media.url,
            mediaID: media.id
        });
    };

    const onChangeBody = value => {
        setAttributes({ body: value });
    };

    const [imageClasses, textClasses, wrapClass] = sortOutCSSClasses(
        alignment || 'left',
        className
    );

    return (
    <>
        {isSelected && (
            <BlockControls key="controls">
                <AlignmentToolbar
                value={alignment}
                onChange={nextAlign => {
                    setAttributes({ alignment: nextAlign });
                }}
                />
            </BlockControls>
        )}

        <div className={wrapClass} key="container">
            <div className={imageClasses}>
                <div className="callout-image">
                <MediaUpload
                    onSelect={onSelectImage}
                    type="image"
                    value={mediaID}
                    render={({ open }) => (
                    <Button
                        className={mediaID ? "image-button" : "button button-large"}
                        onClick={open}
                    >
                        {!mediaID ? __("Upload Image") : <img src={mediaURL} />}
                    </Button>
                    )}
                />
                </div>
            </div>                                  
            <div className={textClasses}>
                <RichText
                    tagName="h2"
                    className="callout-title"
                    placeholder={__("Write a callout title…")}
                    value={title}
                    onChange={onChangeTitle}
                />
                <RichText
                    tagName="div"
                    multiline="p"
                    className="callout-body"
                    placeholder={__("Write the callout body")}
                    value={body}
                    onChange={onChangeBody}
                />
            </div>
        </div>
    </>
    );
},

You can see that the first thing we do is assign our attributes to some local variables to be used within the function.

const {
    className,
    isSelected,
    attributes: { mediaID, mediaURL, body, alignment, title },
    setAttributes
} = props;

The above syntax is object destructuring and is mostly for the ‘less-typing-is-better’ effect. The next few functions are for handling the onChange() events in the editor. Basically when you change any value you want to update your application state. That’s what the setAttributes() method does.

const onSelectImage = media => {
    setAttributes( {
        mediaURL: media.url,
        mediaID: media.id,
    } );
};

A lot of what’s included in the edit() method are event handlers that set state in the block’s attributes object. You’ll also notice the usage of the useEffect() hook in there. That’s a core React hooks method – under the hood Gutenberg is largely a wrapper over React.

The last part of the edit() function is the return statement. In here we have a bunch of JSX code that determines what the Gutenberg interface actually looks like. You can load in some standard components and blocks from the @wordpress/block-editor package.

import {
    RichText,
    MediaUpload,
    BlockControls,
    AlignmentToolbar
} from "@wordpress/block-editor";

Then in your return statement you can use them and assign attribute values:

    return (
        ...
            <div className={textClasses}>
                <RichText
                    tagName="h2"
                    className="callout-title"
                    placeholder={__("Write a callout title…")}
                    value={title}
                    onChange={onChangeTitle}
                />
                <RichText
                    tagName="div"
                    multiline="p"
                    className="callout-body"
                    placeholder={__("Write the callout body")}
                    value={body}
                    onChange={onChangeBody}
                />
            </div>
        ...
    );

The WordPress block editor includes a block called RichText. This is the main block you’ll want to use to make editable areas with. There are options for setting the type of HTML tag it outputs as well as a variety of other settings. The onChange attribute is where you assign your onChange() function. This is important as it’s how you update your block attributes and make everything update in real-time.

save()

So that’s cool and all but we’re not actually saving anything yet. The next part of our custom block is the save() method.

This method defines how you want your block to be displayed on the front-end.

Gutenberg actually does something quite unique with how it saves data. Unless you’re saving data to post_meta the block configuration is serialized in an HTML comment above the block. WordPress appears to filter this out on the front-end, but it’s visible in the database record.

<!-- wp:fancy-block-plugin/fancy-custom-block {"mediaID":4035} -->
<div class="wp-block-fancy-block-plugin-fancy-custom-block bootstrap-block"><div class="wrap-left-"><div class="image-left-"><img class="the-image" src="http://wpdevelop.devtest/content/uploads/2020/01/image-5.jpg"/></div><div class="text-left-"><h2 class="callout-title">Test 2</h2><div class="callout-body"><p>Test</p></div></div></div></div>
<!-- /wp:fancy-block-plugin/fancy-custom-block -->

It’s a bit weird, but maintains backwards compatibility – if you ever disable Gutenberg the content will remain intact.

save: props => {
    const {
        className,
        attributes: { title, mediaURL, body, alignment }
    } = props;

    const [imageClasses, textClasses, wrapClass] = sortOutCSSClasses(
        alignment || "left",
        className
    );

    return (
        <div className="bootstrap-block">
                <div className={wrapClass}>
                    <div className={imageClasses}>
                        {mediaURL && <img className="the-image" src={mediaURL} />}
                    </div>
                </div>
                <div className={textClasses}>
                    <RichText.Content
                        tagName="h2"
                        className="callout-title"
                        value={title}
                    />
                    <RichText.Content
                        tagName="div"
                        className="callout-body"
                        value={body}
                    />
                </div>
        </div>
    );
}

The save() method is actually much simpler than the edit() method and you get the values of the attributes passed in as well. Then it’s mostly a matter of creating your front-end markup and inserting your values. Easy-peasy.

Issues I came across

Alright, so that’s the ‘how’ of creating a custom block, but what about the gotcha’s? Well, it turns out, there’s a few 😢.

The documentation is a little hard to parse. It’s not super easy to understand and is split up in a strange way. I found it hard to track down the documentation for each piece of building a custom block and ultimately just read through the example block code. There’s a lot of digging through code required to understand how things work.

I also found it really annoying working on a block that’s actively changing in code. Every time you reload Gutenberg, you’ll get the “This block appears to have been modified externally…” message because the markup of the block has changed.

I get why it’s throwing the error, but it slows you down.

I also had some strange console errors because SCRIPT_DEBUG was enabled. This constant is required to use the uncompiled version of React locally. Apparently this is a known issue, but it’s a bit crappy seeing console warnings because a WordPress core constant is enabled.

Console error

Further, React devtools are essentially useless as the React component names in Gutenberg are one character names…

React devtools screenshot

All this is to say: there are still some rough edges so your mileage may vary.

Alternatives

Ok, so it’s a bit of work to create a truly custom block. But what if you want to create a block type via a UI? Well, it turns out you can with ACF. ACF Blocks does much of this configuration all within the familiar ACF fields interface. The other benefit is that you can write the display code for ACF Blocks all in PHP.

ACF Blocks are a bit different than a custom created block though. The recommended approach with ACF Blocks is to edit the fields in the sidebar editor and view the preview in the main editor area. It’s a bit of a different experience to creating blocks, but it’s more inline with how ACF has traditionally worked – adding content in fields that are separate from the main editor.

If you’re only interested in rendering field values inside a block, this may be an option for you. You can also style the ACF block with an editor stylesheet so that it appears as it would on the frontend.

ACF Blocks feel more like a ‘hack’ of Gutenberg rather than the official way of creating blocks.

There are also other WordPress plugins that let you ‘assemble’ blocks, like Block Lab and Lazy Blocks. I haven’t tried either, but they may be an option if you’re looking for a minimal-code method for creating a custom block.

After it’s all said and done

So there you have it, how to create ‘custom’ blocks for the WordPress editor (or Gutenberg).

Have you created a custom Gutenberg block yet? What was your impression? Let us know in the comments.





Source link

Post image
Strategy

I need your help with pricing my work : graphic_design


Hey Reddit,

I need your help. I’ve had problems with being consistent with pricing my logos. I recently got requested to make a logo for a friends new photography brand. I’ve charged from $25- $90 in the passed for logos. I dont want to charge her $25 because i think it’s too little for the time i spend making them. I am aiming for $100 but i’m still not sure if that’s appropriate. My guess is that it can take me about about 9-10 maybe even 13 hour max. to make a logo depeding on the designing process.

This is what my previous work looks like for reference:

Post image
Post image
Post image



Source link

Screenshot of the Block options in the WordPress post editor highlighting the options for a quote block. A "Fancy Quote" option is listed under Styles and the custom class name is in an Additional CSS Classes field.
Strategy

How to Use Block Variations in WordPress


WordPress 5.4 was released not so long ago and, along with other improvements and bug fixes, it introduced a feature called Block Variations. I had a chance to use it on one of my recent projects and am so pleasantly surprised with how smart this feature is. I actually think it hasn’t received the attention it deserves, which is why I decided to write this article.

What is a Block Variation?

Block Variations allow developers to define instances of existing blocks. An example that you’ll see below is a quote block. Perhaps your site has three variations of how to display a quote on your site. A Block Variation can be created for each one so that they are all styled differently. This sounds awfully familiar with how Block Styles, but the concept of variations goes a bit further than that, as we’ll see.

How are Block Variations different from Block Styles?

Fair question. Block variations appear in the inserter as separate blocks with unique names and (optionally) icons and can have pre-filled custom attributes, and inner blocks.

Block Styles are designed to alter the look of the block. In fact, a Block Style is a fancy way of adding a custom class to a block using the Block options in the post editor.

The difference is clear when you consider how each one is used in the post editor. Let’s say we register a new Block Style called “Fancy Quote.” We do that by extending the core “Quote” block like this example from the Block Editor Handbook:

wp.blocks.registerBlockStyle(
  'core/quote',
  {
    name: 'fancy-quote',
    label: 'Fancy Quote'
  },
);

This adds a .is-style-fancy-quote class to the Quote block settings in the post editor.

Screenshot of the Block options in the WordPress post editor highlighting the options for a quote block. A "Fancy Quote" option is listed under Styles and the custom class name is in an Additional CSS Classes field.
We now have a Fancy Quote option in the Block options under “Styles” and the class for it filled in for us.

Even though it sort of sounds like it would do the same thing (which it technically can), a Block Variation can be used to pre-fill custom attributes (including custom classes) and inner blocks. They’re actually registered as separate blocks.

Let’s take a closer look at the API and what block variations can do.

Creating a Block Variation

The API for registering Block Variations is very similar that of the Block Style we just looked at:

wp.blocks.registerBlockVariation(
  'core/quote',
  {
    name: 'fancy-quote',
    title: 'Fancy Quote',
  },
);

The registerBlockVariation function accepts the name of the block (in our case it is core/quote) and an object (or an array of objects) describing the variation(s).

The code above doesn’t do much by default, but it does add “Fancy Quote” to the list of available blocks.

Showing the Fancy Quote variation in the WordPress Block Inserter.
We now have two different “quote” blocks available to drop into the post.

To take full advantage of the variation. we need to provide more details in the object describing it. The list is covered in the Make WordPress post, but I’ll share it here and provide additional comments.

  • name – The unique and machine-readable name of the variation. Following the examples on Github and Make post it’s safe to assume that the best practice is to use kebab-case for naming variations.
  • title – A human-readable variation title. This is what appears under the icon in the Inserter.
  • description – A detailed variation description. Appears in the Inserter as well. If empty, the default block description will be used. (Optional)
  • icon – An icon for the variation. Can be a Dashicons slug, an SVG or an object. Follows the same declaration pattern as in registerBlockType. (Optional)
  • isDefault – Indicates whether the current variation is the default one. Defaults to false. In case of our example, if we set it to true, the Fancy Quote block will be the only Quote block available in the inserter. (Optional)
  • attributes – Values that override block attributes. These are block-specific. You can set the level for the Heading block or height for Spacer, for example.
  • innerBlocks – Initial configuration of nested blocks. Only applies to blocks that allow inner blocks in the first place, like Columns, Cover, or Group. We’ll cover this in one of the examples. (Optional)
  • example – Example provides structured data for the block preview. You can set it to undefined to disable the preview shown for the block type. This is the same as the example field in registerBlockType. (Optional) There’s more information available on this parameter.
  • scope – The list of scopes where the variation is applicable. When not provided, it assumes all available scopes. Available options are block and inserter. We’ll cover this in detail in one of the examples.

Many of you may wonder why we need this extra layer of abstraction. Let me try to answer that with a few use cases (one form my recent project).

Use case: Buttons with different widths

Let’s say you have a design system with two types of buttons: Fill and Outline.

Two buttons, one with a green fill and one with a green border. Both say Learn More.
Fill and Outline button styles in the design system

Lucky you, because these are the default styles for buttons in WordPress. No need to register any new styles or hack the editor. All you have to do is write some CSS for each style and call it a day. Life is good and everybody’s happy.

But then you look in the design spec again and notice that there is a little twist. The buttons come in three widths: Regular, Wide, and Full.

The same green buttons but with additional variations at two different widths for a totally of six buttons.
Fill and Outline button styles with different width variations

Dammit! You are a little upset because you now have two options:

  1. Write two extra classes for the new button sizes (say, .is-wide and .is-full), then teach the client to use the Advanced panel in the editor to add those classes and write a manual where you explain what each class does. Or…
  2. Register four(!) new styles that go in the Block options: Fill Wide, Fill Full, Outline Wide, and Outline Full.

Neither of those options are exactly elegant. (BTW, what is Fill Full exactly? Quite an unfortunate mouthful!)

There are two more options that I didn’t include in the list:

  • Filter the button block and add a custom width control to it
  • Build a custom block from scratch.

These obviously feel like heavy lifts for such a simple task.

Enter Block Variations! By adding just two variations, Full and Wide, we can keep things clean and simple:

wp.blocks.registerBlockVariation(
  'core/buttons',
  [
    {
      name: 'wide',
      title: 'Wide Buttons',
      attributes: {
        className: 'is-wide'
      },
  },
  {
      name: 'full',
      title: 'Full Buttons',
      attributes: {
        className: 'is-full'
      },
    }
  ]
);

This is the same as adding a custom class to the Buttons block, but in a neat and elegant way that can be dropped directly into a post from the Block Inserter:

Showing the Wide and Full button variations in the WordPress Block Inserter.
Button variations in the inserter

Life is good and everybody is happy again! So what did we learn from this example?

  • It shows that Block Variations are not designed to replace Block Styles. In fact, they can work pretty well together even if the variation just adds a class to a block.
  • It demos how to register multiple variations in a single declaration.

Use case: Repeating column layouts

Let’s say you are a designer and have a portfolio website with case studies. Each case study has an intro section with the name of the project, client information, and a description of your role on the project. It might look something like this:

Showing three columns, one that says Website Design, one that says Clients, and one says Role. Each one represents a column we want on the page.
The type of work (left), who it was for (center) and your role on it (right)

The problem is that it’s a bit tedious to build this part of the layout every time you create a new portfolio case study — especially because the Client and My Role headings never change. You are only editing the main title and two paragraphs.

With Block Variations, you can create a variation of a core Columns block called Project Intro that will have the columns, and inner blocks already defined. This example is a bit more involved, so we’ll build it out step-by-step.

Let’s start with registering the variation:

wp.blocks.registerBlockVariation(
  'core/columns', {
    name: 'project-intro',
    title: 'Project Intro',
    scope: ['inserter'],
    innerBlocks: [
      ['core/column'],
      ['core/column'],
      ['core/column'],
    ],
  }
);

We are taking this example a bit further than the first one, so why not add a custom portfolio icon from the Dashicons library that’s baked right into WordPress? We do that with the icon property.

wp.blocks.registerBlockVariation(
  'core/columns', {
    name: 'project-intro',
    title: 'Project Intro',
    icon: 'portfolio',
    scope: ['inserter'],
    innerBlocks: [
      ['core/column'],
      ['core/column'],
      ['core/column'],
    ],
  }
);

This will make the block available in the block menu with our icon:

Variation with a custom icon in the Block Inserter.

The next important thing happens on where we add inner blocks:

wp.blocks.registerBlockVariation(
  'core/columns', {
    name: 'project-intro',
    title: 'Project Intro',
    icon: 'portfolio',
    scope: ['inserter'],
    innerBlocks: [
      ['core/column'],
      ['core/column'],
      ['core/column'],
    ],
  }
);

But this only gives us three empty columns. Let’s add starter content and inner blocks to each of them. We can use the same pattern we use to declare a block template in the InnerBlocks component. We can add an object with block attributes as a second element in the array describing the block, and an array of inner blocks as the third element.

The first column will look like this:

['core/column', {}, [
  ['core/heading', { level: 2, placeholder: 'Project Title'} ],
]]

…and the complete block variation is like this:

wp.blocks.registerBlockVariation (
  'core/columns', {
    name: 'project-intro',
    title: 'Project Intro',
    icon: 'portfolio',
    scope: ['inserter'],
    innerBlocks: [
      ['core/column', {}, [
        ['core/heading', { level: 2, placeholder: 'Project Title' }],
      ]],
      ['core/column', {}, [
        ['core/heading', { level: 3, content: 'Client' }],
        ['core/paragraph', { placeholder: 'Enter client info' }],
      ]],
      ['core/column', {}, [
        ['core/heading', { level: 3, content: 'My Role' }],
        ['core/paragraph', { placeholder: 'Describe your role' }],
      ]],
    ],
  }
);

Cool, now we can insert the whole section with just one click. Okay, it’s a few clicks, but still faster than without using the variations.

So what did we learn from this example?

  • And demos how to use the inner blocks within the variation
  • It shows how to define a custom icon for a variation

Use case: Four-column layout

You already know that columns are a default block type, and that there are a handful of options for different types of columns. A four-column layout isn’t one of them, so we can build that. But this introduces a new concept as well: scoping in context of block variations.

Some core blocks, like Columns, already offer variations out of the box. You can choose one of them after you insert the block on the page:

Showing the columns block inserted with 5 different layout options to display up to 3 columns at varying widths.
Block-scoped variations

Let’s say you use a four-column layout on your website as often as you use two-column one. That’s unfortunate, because there is no shortcut button to create four-column layout. Creating one is a bit annoying because it takes extra clicks to get to the Columns control after the block is inserted:

Showing the slider control to change the number of columns in the Block settings.

So, what can you do to improve this workflow? Right, you can add a Block Variation that will create a four-column layout. The only difference this time, compared to previous examples, is that it makes much more sense to include this variation inside the block placeholder, next to all other column layouts.

That is exactly what the scope option is for. If you set it to [block], the variation will not appear in the Block Inserter but in the variations once the block has been inserted.

wp.blocks.registerBlockVariation(
  'core/columns', {
    name: 'four-columns',
    title: 'Four columns; equal split',
    icon: <svg ... />,
    scope: ['block'], // Highlight
    innerBlocks: [
      ['core/column'],
      ['core/column'],
      ['core/column'],
      ['core/column'],
    ],
  }
);
Four-column layout variation scoped to the block.
Hey, now we have a four-column option!

Isn’t that sweet?!

I’ve omitted the full SVG code for the icon, but it’s available if you need it.

To sum up scope: If it isn’t declared, the variation will appear in the Block Inserter and inside the block placeholder — specifically for blocks that support block-scoped variations. 

If we were to remove the scope parameter from the example above, here’s how the variation would appear in the inserter:

Four-column block variation in the block inserter.
Keep in mind that the icon sizes for variations within the block and and the block icons size are different. The custom icon for columns was intended for the block scope, that’s why it looks a bit out-of-place in this example.

So what did we learn from this example?

  • It explains the difference between the block and inserter scope for the variation.
  • We learned how to use SVG for variation icon.

That’s it!

As you can see, Block Variations are pretty powerful for building a lot of things, from different variations of buttons to complete page layouts.

I’d like to wrap this up with a quick recap of different APIs for block customizations and when to use them:

  • Use Block Styles if you need to alter the appearance of the block and adding a CSS class is enough for that.
  • Use Block Variations if you need to specify the default attributes for the block and/or add inner blocks to it.
  • If that’s not enough and you need to change the markup of the block, you are probably looking into filtering the block or creating a new one from scratch.

If you’ve had a chance to play with Block Variation, let me know what you think of them in the comments!

Resources



Source link

Post image
Strategy

[API Design] Getting notified of changed data from multiple …


I’m working on making a SPA dashboard page, where there are multiple isolated components, each being fed by a dedicated endpoint (shown by hollow colored boxes), for example user info, his uploads, and some other meta information. Except that some endpoints have some overlapping data (depicted as solid red boxes), example, last updated field, or something that only backend can know, so front-end can’t keep them synced locally.

The page loads, and each component feeds itself by making a `GET` request to its respective endpoint. However, one of the components has an edit field (shown by solid light blue box), which when saved triggers a `PUT` request, that can, and will change the value of red boxes, i.e the shared information coming from different endpoints.

My goal is to keep the components isolated as they are, without knowing the “relationship” described above. How can I rig my backend or frontend (or both), where the green box and orange box “update” to reflect the changed red?

Context and constraints –

– No full page reload (some of the endpoints are already quite “heavy”)

– No “single-store”, like Redux, we’re using Angular w/ services and rxjs

– No websockets, or polling or anything along those lines (pure `GET`, `PUT` etc.)

– We’d prefer to keep frontend agnostic of this relationship directly

How can this be achieved?

Post image



Source link