Orange-OpenSource / ouds-ios

A SwiftUI components library with code examples for Orange Unified Design System
https://ios.unified-design-system.orange.com/
MIT License
5 stars 1 forks source link

Common cross-libraries token architecture #33

Closed julien-deramond closed 1 month ago

julien-deramond commented 2 months ago

Description

This issue aims to define and implement a common cross-libraries token architecture for the tech libraries: Android, iOS, Flutter, and web.

Design tokens are defined on the design side via Figma, and Figma's capability to handle and define tokens is more limited compared to what we can do on the development side. So there will be a slight discrepancy between the design and the tech side.

In essence, Figma’s token mechanism requires designers to pre-create a set of tokens that may not be inherently useful for the core part of our tech libraries.

On the technical side, it is important to define what is available by default:

This must be considered at different levels of the token stack, including primitive raw values, semantic tokens, and component tokens.

Our objective is to establish a common cross-library architecture to ensure uniformity in terminology and implementation.

[!NOTE]
Closing this issue will unlock all the "Tokens: *" issues in this repository

[!IMPORTANT]
The possibility to add semantic tokens from outside is a topic we'd like to deep dive in. However, creating prototypes for that now, is considered as too complex as we don't have yet libraries handling tokens. The safest approach is to limit the scope of this study.

Based on the study done on the Android side in https://github.com/Orange-OpenSource/ouds-android/issues/34, in terms of customization, our libraries will need to handle first only a light customization such as white-label themes.

Said differently, our libraries will define only the raw/semantics/components tokens needed for the global core Orange theme. And only these tokens would be updatable. It also means that our tech libraries won't contain at first the concepts coming from B-Brands that are not used in the core library. Basically, it's like what has been done in ODS, but with a new constraint: the theme should be extendable. An extended theme can rewire semantic tokens with its own raw tokens and component tokens with semantic tokens. It defines its own raw tokens (often generated from Figma) and doesn't need to access the raw tokens of the parent theme.

It corresponds to the schema presented in the "Android architecture" section in https://github.com/Orange-OpenSource/ouds-android/issues/34.

Implementation

As a summary, for the implementation part:

Study

Technical details

Main goals

The technical architecture must tackle several topics like the user experience, the developer experience, the ecodesign principles and also the source code maintanability and efficiency. We should also keep in mind a lof of tokens will be added in the code base (hundreds or thousands) and plenty of them must be overridable, so the library must be robust, tested and scalable.

Architecture of the Swift Package

First, the OUDS iOS library, as a Swift Package should expose fine grained products with their own responsibilities, e.g. several modules for: raw tokens, semantic tokens, components takes, shared elements for themes, Orange theme, comonents, modules and foundations. Thus developers using the OUDS iOS library will be able to pull in Xcode the modules they want and keep their code base clean. Having several modules will help to test the codebase and keep things clean by splitted repsonsabilites and reducing the frictions between all the parts of the library.

The module dedicated to OUDS modules must rely on the OUDS components. Theses components laod their style using component tokens and the Orange theme as a default theme. The Orange theme is based on shared elements mainly defining what a theme is. Any semantic tokens and raw tokens are also used by themes.

OUDS - iOS - Chart - Package diagram (v2)
Architecture of themes

The object oriented paradigme implementation in Swift is a bit different that the one found with Java or Kotlin. For example we do not have abstract classes and Swift relies a lot in composition with protocols and extensions. Thus a theme can be composed using protocols containing as properties the semantics tokens. These tokens must also be overridable. Any defailt values and logic can be in a kind of fake abstract class named OUDSTheme. This theme will be then subclassed using OOP inheritance in the real default theme named OrangeTheme. Any other theme can be a subclass of OrangeTheme (e.g. country specific theme) or OUDSTheme (for white label).

Themes must be able to override semantic tokens and components tokens, and use its own raw tokens without sharing them to other themes. The exiting raw tokens, shared between all themes, are not overridable because their definitions are frozen.

OUDS - iOS - Chart - Themes classes diagram (v2)
Architecture of components tokens

Components tokens can be defined so as to be used in components and make the glue with semantic tokens. They mus tbe overridable by themes.

Architecture of semantic tokens

Semantic tokens are used by components tokens and shared definition of themes. They are spltited in kind of families, i.e. borders, dimensions, colors, spacings, elevations, sizings, opacities, grids and typographies. For some of theses tokens, like for borders, subfamilies can be defined likes width, radius and style. Finally, any of these raw tokens is associated to a raw value which will be - in the end - applied to SwiftUI views inside components. Type aliases will help to make a simple math between any semantic tokens and raw tokens.

OUDS - iOS - Chart - Semantic token classes diagram (v2)
Architecture of raw tokens

To avoid to define data structures which can be heavy in the end like class or structs, only primitives types are used for raw tokens (thus indirectyl for higher level tokens like semantics and components ones). Use each time type aliases whill help develoeprs to find easily which type of tokens they want without using bigger objects.

OUDS - iOS - Chart - Raw token classes diagram (v2)
pylapp commented 1 month ago

@julien-deramond Pour ne pas polluer la définition de cette carte, je mets ci-dessous pour traçabilité mes commentaires :)

D'une part, j'ai modifié ce ticket en décrivant l'architecture du Swift package OUDS iOS, y apportant notamment des schémas en reprenant des notions UML. J'ai pu également testé le passage à l'échelle de notre architecture en générant, via un script Shell, les lignes de codes Swift pour définir l'ensemble des raw tokens et semantic tokens que l'on peut trouver dans l'outil de tableau blanc de l'équipe de design. Cela a permis finalement de concevoir quelque chose de souple. À voir toutefois dans la durée ce que ça donnera : entre la génération de tokens en Swift via un JSON exporté avec style-dictionary, le besoin d'avoir tout surchargeable, et la quantité astronomique de tokens, conjugués avec le besoin d'avoir une base de code propre, testable, facilement utilisable et pas lourde pour les utilisateurs, peut-être devrons-nous faire face à des soucis structurels. Mais pour l'instant, la version actuelle sur la branche 33-define-tokens-architecture comporte 1 059 tokens (components, semantics et raws confondus générés en 25 secondes) et ça tient la route.

Il est possible de définir un thème "complet" en surchargeant chacun de ces tokens et en étant assez masochiste.

Sinon, le thème de base est le "thème Orange" embarquant ses propres raw tokens de couleurs et surchargeant certains semantic tokens. Ce thème peut être dérivé aussi.

L'architecture actuelle de manière générale va définir un thème "abstrait" qui aura des valeurs par défauts ; ici naïvement en reprenant un à un toutes les définitions des tokens du tableau blanc de l'équipe de design. Le thème "Orange" est basé sur ce thème "abstrait" mais séparé afin qu'il embarque ses propres raws tokens sans les exposer aux êtres thèmes frêres.

J'ai pu faire une app à part pour embarquer la lib OUDS iOS et définir un thème basé sur celui Orange, tout est bon. Sonc ode est en pièce jointe.

L'app de démo du projet OUDS iOS comporte un composant banal (vaguement inspiré du composant forms input text du tableau blanc), auquel on applique le thème Orange ou un thème "local" (défini en dehors de la lib iOS OUDS).

Bref, la version sur cette branche de la lib iOS permet de répondre aux besoins suivis :

Restera, pour d'autres jalons, à creuser la génération de la documentation avec DooC pour Swift comme vu par @ludovic35 et aussi les tests unitaires. En effet, même si à terme les tokens seront générés, on peut faire la supposition que les variables auront une immense majorité du temps les mêmes noms mais pas les mêmes valeurs. Pour certains raw tokens on peut tester les relations d'ordre (un fontSize100 devant être plus petit qu'un fontSize200 par exemple). On peut aussi vérifier que les tokens sont bien surchargeables entre un thème dérivé et son thème parent. Ainsi, avoir des tests unitaires câblés sur la chaîne de CI/CD pourra nous assurer que le code Swift généré et intégré n'apporte pas des régressions de ce genre. À investiguer, même pas bien compliqué à générer.

UML charts, tokens generation Shell script.zip

HelloWorld.zip

cc @paulinea @florentmaitre @ludovic35 @Tayebsed93

pylapp commented 1 month ago

Ci-joint, une vidéo de l'app de démo avec une bascule de thème (Orange / quelconque) et un changement de mode (clair / sombre), ainsi qu'une capture d'écran de l'app banale "Hello world" avec ses propres thème et raw tokens appliqués au même composant exposé via la lib OUDS iOS.

vidéo et capture d'écran.zip

pylapp commented 1 month ago

You can find the sample component with basic themes in the last TestFlight alpha version upload.

📣 New TestFlight ALPHA upload 🚀

pylapp commented 1 month ago

May be linked with https://github.com/Orange-OpenSource/ouds-ios/issues/5

pylapp commented 2 weeks ago

FYI this issue can be seen as "delviered" since version v0.0.0 even if the architecture might evolve a bit in the future (see #79)