[High] Add `tagName`, `style`, and `attributes` overrides

Any rendered element will take the form <tagName style={style} {...attributes} />, where the class property will effectively just proxy values for style.

Uipkg currently offers a very reliable way of compiling layouts to components with associated styles, and building on top of this is how we will ensure any given layout or component variant can be compiled to a functional element. We simply need a section called Overrides below the Content section, with tagName, style, and attributes overrides, similar to how the Content view currently looks.

This functionality is necessary to handle behavior like working <input> fields, overflow: "scroll", and other things that Figma cannot allow us to specify in pure design. There have to be escape hatches in order to guarantee you can export any component you can correctly configure.

Here’s the emitted module for the test component, just a flex rectangle with some background:

import React from "react";
import styled from "@emotion/styled";
interface Props {}

const TestComponent = (props: Props) => {
  return (
    <Container>
      <Rectangle1 />
    </Container>
  );
};

const Container = styled.div`
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: flex-start;
  align-self: stretch;
  flex-grow: 1;
  position: relative;
`;

const Rectangle1 = styled.div`
  align-self: stretch;
  flex-grow: 1;
  background: #ffc8c8;
`;

export default TestComponent;

With { tagName: "input", attributes: { type: "checkbox" }, style: { overflow: "hidden" } } overrides, the only changes needed to the emitted output are:

const TestComponent = (props: Props) => {
  + const tagName = "input";
  + const attributes = { type: "checkbox" };
  + const style = { overflow: "hidden" };
  return (
   - <Container>
   + <Container as={tagName} style={style} {...attributes}>
      <Rectangle1 />
    </Container>
  );
};

Comparison with Amplify Studio

Amplify Studio’s UI Library does support:

  • element style overrides
  • element attributes overrides
  • element tagName overrides de facto, in some cases (specifically, elements for which Amplify has created a primitive, i.e. <input>), though this is not customizable as proposed here

The recommended implementation is a lot more flexible than Amplify’s, and importantly means you could design any valid layout in Figma and compile to a layout with browser-native functionality via <input>, <form>, <select>, etc.. The extensibility is important and also requires the least work to do. Simply give users a way to plug in, and Figma’s copy-paste will more or less handle the setup cost as component overrides will only need to be set up once.

Tackling the big features! Yeah, this is something that I want to support, but having it customisable by the user in the properties tab will be quite a lot of work.

I was actually wondering myself how to introduce escape-hatches for uipkg. I can probably introduce a specific prop that users can leverage to change behaviour at runtime.

End user API could look like this:

<Component
  overrides={{
    elementName: { as: 'input', type: 'text', onChange: () => {/* */} },
    anotherElementName: { as: 'form', style: { overflow: 'scroll'  } } 
  }}
/>

Using this example you could pass arbitrary props to any element in the component and customise it to your liking. In the emitted output it would just spread the overrides from the passed props on to the elements.

Would an API like this cover your use case at the moment?