Categories
components

How we use web components

At GitHub, we pride ourselves on delivering a first-class developer experience. A considerable part of our work is on our front end, which we strive to keep as lightweight, fast, and accessible as possible. For a product as large as GitHub, this can be quite the task. Like many front-end codebases, we leverage components, independent,…

At GitHub, we pride ourselves on delivering a first-class developer experience. A considerable part of our work is on our front end, which we strive to keep as lightweight, fast, and accessible as possible. For a product as large as GitHub, this can be quite the task. Like many front-end codebases, we leverage components, independent, isolated, and reusable pieces of code that allow application teams to deliver high fidelity UI quickly and efficiently while still keeping to our high standards of quality.

We’re using Web Components in a big way at GitHub. We have over a dozen open-source Web Components and with dozens more that are closed source.

How we got here

When GitHub launched over a decade ago, we had a modest front-end codebase that mostly used jQuery. Ten years and nearly 85,000 lines of code later, we had a large front-end codebase that was starting to show growing pains. We ultimately transitioned away from jQuery (for reasons which we detailed in a blog post at the time) and started using new technologies which could better solve our problems.

We began to dabble with a new technology called Web Components, a set of native browser technologies that allow the development of customized HTML elements, progressively enhanced with JavaScript.

We chose to use Web Components because our codebase was already structured into component-like behaviors. Still, as the GitHub monolith grew in size, we saw the need to implement better encapsulations before the front-end became unmanageable – and Web Components fit the bill. Web Components offered better portability and encapsulation than our existing JavaScript behaviors. We were happy to experiment with Web Components alongside our existing front-end infrastructure since it doesn’t incur any upfront cost or “buy-in” to a specific framework.

Our first two custom elements shipped in 2014: and , which show times and dates in friendly formats, and , which allows us to lazy load HTML fragments. Slowly we realized just how powerful these elements could be and began replacing design patterns within the codebase wholesale, such as replacing our “facebox” modal dialog pattern with . Our components now range from very generic, multi-purpose behaviors like to specific single-purpose components such as the element and its siblings.

For the power Web Components affords, there are still pain points and pitfalls. With such a large codebase owned by hundreds of engineers across dozens of teams, we need to provide as much support and tooling as possible, encoding best practices without manual code review becoming a bottleneck.

Improving how we author components

To make engineers effective at writing high-quality Web Components—and encourage best practices—we’ve been working on a few tools to make authoring Web Components much easier.

ViewComponent

We’ve been transitioning our Rails code to using ViewComponent, a framework for building reusable components within Rails. ViewComponent goes hand-in-hand with Web Components since a ViewComponent can have a one-to-one relationship with a Web Component, allowing our developers to work on a single abstraction for both front-end and backend.

Catalyst

Catalyst, our open source library that makes it easier to write web components, has been a driving force that ties together some of our best practices. Catalyst leverages TypeScript to add decorators, which save on a lot of the boilerplate necessary to write Web Components.

Catalyst took inspiration from the excellent Stimulus library, and Google’s LitElement. It is designed to answer the specific set of needs our developers require. Our internal developer experience surveys have shown success in providing a substantial improvement in authoring code over legacy patterns.

You can read more about Catalyst and its conventions, patterns, and anti-patterns in our guide.

Tooling

We provide a set of open-source linter configurations for developers. For general code practices we have eslint-plugin-github. We also have eslint-plugin-custom-elements, which provides further checks for authoring Web Components. Extracting these to open source repositories allows us to remove code from the monolith but remain consistent.

We also have internal tests to verify that developers follow best practices and ensure that they don’t continue to use deprecated patterns and behaviors. One of our tests makes sure that a deprecated “facebox” pattern isn’t introduced to the codebase and suggests using a element as an alternative.

class FaceboxDeprecationTest < Test::Fast::TestCase
  EXPECTED_NUMBER_OF_FACEBOXES = 44

  # Find facebox triggers set with rel=facebox, in either HTML attributes or
  # as part of hash assignment (for rails helpers)
  REGEX_FOR_FACEBOX_BINDING = %r|rels*[=:]>?s*["']?facebox|
  REGEX_FOR_DATA_FACEBOX = %r|data-faceboxs*=>?s*|

  def test_limit_facebox
    actual_rel_facebox = grep(REGEX_FOR_FACEBOX_BINDING, options: %w[-In], paths: %w[app//*.erb])
    actual_data_facebox = grep(REGEX_FOR_DATA_FACEBOX, options: %w[-In], paths: %w[app//*.erb])
    count = actual_rel_facebox.count("n") + actual_data_facebox.count("n")

    assert_operator count, :<=, EXPECTED_NUMBER_OF_FACEBOXES, <<-EOL
It looks like you added a facebox. Please use  instead.

If you must increment EXPECTED_NUMBER_OF_FACEBOXES in this test, please
/cc @github/ui-frameworks-reviewers in your pull request, as we may be able to help! Thanks.
EOL

    assert_equal EXPECTED_NUMBER_OF_FACEBOXES, count, <<-EOL
It looks like you removed a facebox. YOU ARE AWESOME! 💖 💖 💖 💖 💖
Please decrement EXPECTED_NUMBER_OF_FACEBOXES in this test and treat yourself to
something special.  You deserve it.
EOL
  end
end

Our new l

Read More

Leave a Reply

Your email address will not be published. Required fields are marked *