Repository: motion-canvas/motion-canvas
File: packages/2d/src/components/Layout.ts (TypeScript)

Code before:
  @initial(null)
  @signal()
  public declare readonly basis: SimpleSignal<FlexBasis, this>;
  @initial(0)
  @signal()
  public declare readonly grow: SimpleSignal<number, this>;
  @initial(1)
  @signal()
  public declare readonly shrink: SimpleSignal<number, this>;
  @initial('nowrap')
  @signal()
  public declare readonly wrap: SimpleSignal<FlexWrap, this>;

  @initial('normal')
  @signal()
  public declare readonly justifyContent: SimpleSignal<FlexJustify, this>;
  @initial('normal')
  @signal()
  public declare readonly alignItems: SimpleSignal<FlexAlign, this>;
  @initial(0)
  @signal()
  public declare readonly gap: SimpleSignal<Length, this>;
  @signal()
  public declare readonly rowGap: SimpleSignal<Length, this>;
  protected getDefaultRowGap() {
    return this.gap();
  }
  @signal()
  public declare readonly columnGap: SimpleSignal<Length, this>;
  protected getDefaultColumnGap() {
    return this.gap();
  }

  @defaultStyle('font-family')
  @signal()
  public declare readonly fontFamily: SimpleSignal<string, this>;
  @defaultStyle('font-size', parseFloat)
  @signal()
  public declare readonly fontSize: SimpleSignal<number, this>;
  @defaultStyle('font-style')
  @signal()
  public declare readonly fontStyle: SimpleSignal<string, this>;
  @defaultStyle('font-weight', parseInt)
  @signal()
  public declare readonly fontWeight: SimpleSignal<number, this>;
  @defaultStyle('line-height', parseFloat)
  @signal()
  public declare readonly lineHeight: SimpleSignal<Length, this>;
  @defaultStyle('letter-spacing', i => {
    if (i === 'normal') {
      return 'normal';

Review comment (bug):
Having both a horizontal and vertical gap makes me think a compound signal would be better here, especially since this will result in:
```tsx
object.rowGap(10);
object.gap(20);
debug(object.rowGap()); // 10
```

Code after:
  public declare readonly direction: SimpleSignal<FlexDirection, this>;
  @initial(null)
  @signal()
  public declare readonly basis: SimpleSignal<FlexBasis, this>;
  @initial(0)
  @signal()
  public declare readonly grow: SimpleSignal<number, this>;
  @initial(1)
  @signal()
  public declare readonly shrink: SimpleSignal<number, this>;
  @initial('nowrap')
  @signal()
  public declare readonly wrap: SimpleSignal<FlexWrap, this>;

  @initial('normal')
  @signal()
  public declare readonly justifyContent: SimpleSignal<FlexJustify, this>;
  @initial('normal')
  @signal()
  public declare readonly alignItems: SimpleSignal<FlexAlign, this>;
  @initial(0)
  @vector2Signal({x: 'columnGap', y: 'rowGap'})
  public declare readonly gap: Vector2LengthSignal<this>;
  public get columnGap(): Signal<Length, number, this> {
    return this.gap.x;
  }
  public get rowGap(): Signal<Length, number, this> {
    return this.gap.y;
  }

  @defaultStyle('font-family')
  @signal()
  public declare readonly fontFamily: SimpleSignal<string, this>;
  @defaultStyle('font-size', parseFloat)
  @signal()
  public declare readonly fontSize: SimpleSignal<number, this>;
  @defaultStyle('font-style')
  @signal()
  public declare readonly fontStyle: SimpleSignal<string, this>;
  @defaultStyle('font-weight', parseInt)
  @signal()
  public declare readonly fontWeight: SimpleSignal<number, this>;
  @defaultStyle('line-height', parseFloat)
  @signal()
  public declare readonly lineHeight: SimpleSignal<Length, this>;
  @defaultStyle('letter-spacing', i => {
    if (i === 'normal') {
      return 'normal';
    }
    return parseFloat(i);
  })