design-tokens / community-group

This is the official DTCG repository for the design tokens specification.
https://tr.designtokens.org
Other
1.53k stars 62 forks source link

Reference composite type attributes individually #148

Open bheston opened 2 years ago

bheston commented 2 years ago

The guidance doesn't specify whether individual attributes of a composite type can be referenced individually. I suspect the expectation is that they can't, but I'm not sure that's obvious from a tooling or authoring standpoint.

For instance, adapting the typography example:

{
  "type styles": {
    "base": {
      "$type": "typography",
      "$value": {
        "fontFamily": "Roboto",
        "fontSize": "14px",
        "fontWeight": "400",
        "letterSpacing": "0px",
        "lineHeight": "1"
      }
    },
    "heading": {
      "$type": "typography",
      "$value": {
        "fontFamily": "{type styles.base.fontFamily}",
        "fontSize": "18px",
        "fontWeight": "700",
        "letterSpacing": "{type styles.base.letterSpacing}",
        "lineHeight": "{type styles.base.lineHeight}"
      }
    }
  }
}

Or applying only a portion of a type definition to a downstream token, or referencing only the width or color of a border, etc.

This could be refactored to reference other non-composite tokens, but this format is more concise and may better represent how people think about their values, that is, in the context of a "base" usage with relative modifiers. For instance, with mathematical operations, the heading fontSize could be {type styles.base.fontSize} + 4.

Related, I see potential use for something like custom composite types discussed in #54 as well as something more functional as being discussed in #88. In both cases the models represent a set of attributes where only a few are referenced downstream, that is, the output values from some transformation.

I support explicitly declaring that aliases MAY resolve to individual composite type attributes. If for some reason that doesn't work, we should explicitly declare that they MAY NOT.

drwpow commented 2 years ago

+1 this would be useful for us as well. Just to give a real-world example we just hit with shadows, we wanted to get more use out of our base shadow:

{
  "elevation": {
    "$type": "shadow",
    "near": {
      "$value": {
        "offsetX": "0",
        "offsetY": "3px",
        "blur": "5px",
        "spread": "0",
        "color": "#4f4b9326"
      }
    }
  }
}

The shadow as defined uses a top light source, but we had a scroll container we wanted to apply an inner shadow to, and we wanted to apply the same shadow to the left or right edge to create consistency. This would involve extracting the offsetX and offsetY values separately and swapping those (or inverting them) while keeping the other values the same.

In the meantime, we could create 1 shadow for each direction (up, down, left, right), but that’s much more unwieldy.


As to the proposed syntax, I like it. A keyword paired with the alias syntax seems great.

I do wonder if the current syntax of using . to separate groups/tokens would be confusing to reference a specific property on a token. Are there any thoughts around using another character there? e.g.

"{type styles.base#fontFamily}"

I’m not necessarily stuck on the # character, but it would clarify base is a token and fontFamily is a composite property. I’m not advocating for this syntax necessarily, just some distinction between token & property.

romainmenke commented 2 years ago

also see : https://github.com/design-tokens/community-group/issues/126

This would become a non-issue if composite types where implemented as typed groups. Each sub value would be its own token. Some translation tools might just assign special meaning or function to the typed group.

for example :

{
  "type styles": {
    "heading-level-1": {
      "$type": "typography",
      "fontFamily": { "$value": "Roboto", "$type": "string" },
      "fontSize": { "$value": "42px" },
      "fontWeight": { "$value": 700 },
      "letterSpacing": { "$value": "0.1px" },
      "lineHeight": { "$value": 1.2 }
    }
  }
}
CITguy commented 1 year ago

for example :

{
  "type styles": {
    "heading-level-1": {
      "$type": "typography",
      "fontFamily": { "$value": "Roboto", "$type": "string" },
      "fontSize": { "$value": "42px" },
      "fontWeight": { "$value": 700 },
      "letterSpacing": { "$value": "0.1px" },
      "lineHeight": { "$value": 1.2 }
    }
  }
}

The biggest problem I see with this example is that there's no clear differentiation between a composite property and a group token. The basic rule for whether data is considered a Token is the presence of the $value property. Without an explicit $value for the composite token, given the algorithm for resolving $type inheritance from a group, the configuration above would result in 5 separate tokens (not 1 as probably intended) and 4 of them would resolve as "$type": "typography".

The resolved tokens would be as follows...

{
  "type styles.heading-level-1.fontFamily": {
    "$type": "string",
    "$value": "Roboto"
  },
  "type styles.heading-level-1.fontSize": {
    "$type": "typography",
    "$value": "42px"
  },
  "type styles.heading-level-1.fontWeight": {
    "$type": "typography",
    "$value": 700
  },
  "type styles.heading-level-1.letterSpacing": {
    "$type": "typography",
    "$value": "0.1px"
  },
  "type styles.heading-level-1.lineHeight": {
    "$type": "typography",
    "$value": 1.2
  }
}
drwpow commented 3 months ago

The biggest problem I see with this example is that there's no clear differentiation between a composite property and a group token. The basic rule for whether data is considered a Token is the presence of the $value property. Without an explicit $value for the composite token …

Agree. (Also sorry for bringing up old thread, but wanted to push this forward). I’d like to propose what I think you’re suggesting, which is just making the $value explicit, and part of the alias (which seems like it would work because $value is a reserved word in the spec for this very reason):

{
  "type styles": {
    "heading-level-1": {
      "$type": "typography",
      "fontFamily": { "$value": "Roboto", "$type": "string" },
    },
    "heading-level-2": {
      "$type": "typography",
      "fontFamily": "{type styles.heading-level-1.$value.fontFamily}",
    }
  }
}