{"id":15628377,"url":"https://github.com/yonaskolb/stylist","last_synced_at":"2025-06-21T17:39:19.842Z","repository":{"id":62456191,"uuid":"100748721","full_name":"yonaskolb/Stylist","owner":"yonaskolb","description":"Define UI styles for iOS apps in a hot-reloadable yaml or json file","archived":false,"fork":false,"pushed_at":"2022-02-06T06:56:07.000Z","size":847,"stargazers_count":298,"open_issues_count":4,"forks_count":18,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-02T02:40:29.682Z","etag":null,"topics":["css","hot-reload","ios","style","styling","swift","tvos","ui","yaml"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yonaskolb.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-08-18T20:42:03.000Z","updated_at":"2025-02-22T05:54:22.000Z","dependencies_parsed_at":"2022-11-01T23:33:50.808Z","dependency_job_id":null,"html_url":"https://github.com/yonaskolb/Stylist","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yonaskolb%2FStylist","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yonaskolb%2FStylist/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yonaskolb%2FStylist/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yonaskolb%2FStylist/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yonaskolb","download_url":"https://codeload.github.com/yonaskolb/Stylist/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248008630,"owners_count":21032556,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["css","hot-reload","ios","style","styling","swift","tvos","ui","yaml"],"created_at":"2024-10-03T10:22:18.792Z","updated_at":"2025-04-09T09:10:46.693Z","avatar_url":"https://github.com/yonaskolb.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"#  Stylist 🎨\n\n[![Build Status](https://img.shields.io/circleci/project/github/yonaskolb/Stylist.svg?style=for-the-badge)](https://circleci.com/gh/yonaskolb/Stylist)\n![Platforms](https://img.shields.io/cocoapods/p/Stylist.svg?style=for-the-badge)\n[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=for-the-badge)](https://github.com/Carthage/Carthage) [![CocoaPods version](https://img.shields.io/cocoapods/v/Stylist.svg?style=for-the-badge)](https://cocoapods.org/pods/Stylist)\n[![license](https://img.shields.io/github/license/yonaskolb/Stylist.svg?style=for-the-badge)](https://github.com/yonaskolb/Stylist/blob/master/LICENSE)\n\nStylist lets you define UI styles for iOS apps in a hot-reloadable external yaml or json theme file\n\n- ✅ Define styles in **external theme files**\n- ✅ Apply styles **programmatically** or via **Interface Builder**\n- ✅ **Hotload** themes to see results immediately without recompiling\n- ✅ Apply styles to any **UIView**, **UIViewController**, **UITabBar**, and your own **custom** classes\n- ✅ Apply styles by style **names** or **classes**\n- ✅ Apply styles when contained in certain view **hierarchies**\n- ✅ **Swap** entire themes on the fly\n- ✅ Built in style properties for all popular **UIKit** classes\n- ✅ Reference **Theme variables** for commonly used values\n- ✅ Include styles **within other styles**\n- ✅ Define **custom** strongly typed properties and custom parsing to dynamically set any property\n\nExample theme:\n\n```yaml\nvariables:\n  primaryColor: \"DB3B3B\" # hex color\n  headingFont: Ubuntu # reference loaded font\nstyles:\n  MyApp.MyViewController: # applied to MyViewController class\n    view: # access the view\n      tintColor: $primaryColor # reference a variable\n    navigationBar: # access the navigation bar\n      barTintColor: red # use color name\n      titleColor: white\n  UIButton:\n    backgroundImage: buttonBack # set image\n    backgroundImage:highlighted: buttonBack-highlighted # for highlighted control state\n  MyApp.Section:\n    styles: [themed] # include other styles\n    axis(horizontal:regular): horizontal # restrict by size class\n    axis(horizontal:compact): vertical\n  MyApp.Section UIStackView UILabel: # View containment selector\n    textAlignment: right # use enums\n  primaryButton:\n    textColor: \"55F\" # shorted hex value\n    contentEdgeInsets: [10,5] # set simplified UIEdgeInsets\n    font(device:iphone): $headingFont:16 # reference font variable and change size\n    font(device:ipad): $headingFont:22 # restrict to device\n  secondaryButton:\n    cornerRadius: 10 # set layer properties\n    textColor: customColor # use named color\n    font: 20 # use system font\n    contentEdgeInsets: 6 # set UIEdgeInsets using a single value\n  sectionHeading:\n    font: title 2 # use UIFontTextStyle\n    font: darGray:0.5 # built in color with alpha\n  content:\n    font: Arial:content # Use custom font with UIFontTextStyle\n  themed: # style is referenced in other styles\n    tintColor: $primaryColor\n```\n- [⬇️ Installing](#installing)\n- [⚒ Usage](#usage)\n  - [Loading a Theme](#loading-a-theme)\n  - [Setting a Style](#setting-a-style)\n  - [Hot Reloading](#hot-reloading)\n- [🎨 Theme](#theme)\n  - [Style Selectors](#style-selectors)\n  - [Styles References](#style-references)\n  - [View hierarchy styles](#view-hierarchy-styles)\n  - [Style Context](#style-context)\n- [🖌 Style Properties](#style-properties)\n- [⚙️ Custom Properties](#custom-properties)\n- [⚙️ Custom Styleable class](#custom-styleable-class)\n\n## ⬇️ Installing\n\n### Cocoapods\nAdd the following to your `podfile`\n\n```sh\npod 'Stylist'\n```\n\n### Carthage\nAdd the following to your `Cartfile`\n```sh\ngithub \"yonaskolb/Stylist\"\n```\n\n## ⚒ Usage\n\nMake sure to import Stylist\n\n```swift\nimport Stylist\n```\n\n### Loading a Theme\nTo load a Theme use:\n\n```swift\nStylist.shared.load(path: pathToFile)\n```\n\nYou can load multiple themes, and they will all be applied as long as they have different paths.\n\nYou can also load a Theme manually and then add it by name, allowing you to swap themes at runtime.\n\n```swift\nlet theme = try Theme(path: pathToTheme)\nStylist.shared.addTheme(theme, name: \"mainTheme\")\n```\n\n### Setting a Style\nClass styles will be applied to `UIView` when they are added to a superview, and to `UIViewController` when `viewDidLoad()` is called.\n\nTo set a custom style on a Styleable class, simply set its `style` property. You can set multiple styles by comma separating them.\n\n#### Programmatically\n\n```swift\nmyView.style = \"myStyle\"\notherView.style = \"myStyle,otherStyle\"\n```\n\n#### Interface Builder\n\nStyles can be set in Interface Builder in the property inspector\n\n\u003cimg src=\"Resources/IB_styleable_view.png\" width=\"247\"\u003e\n\n### Hot Reloading\nYou can choose to watch a Theme files which means that whenever that file is changed the styles are reloaded. These changes can also be animated!\n\nThemes can live at a remote url allowing you to update styles remotely.\n\nHotloading can be very useful while developing, as you can make changes to your styles on the fly without recompiling and see the results animate in instantly! To watch a file simply call `watch` on stylist and pass in a URL to a local file on disk or a remote url:\n\n```swift\nStylist.shared.watch(url: fileOrRemoteURL, animateChanges: true) { error in\n  print(\"An error occurred while loading or parsing the file: \\(error)\")\n}\n```\nIf an error occurs at any time the `parsingError` callback will be called with a `ThemeError`, which will tell you exactly what went wrong including any formatting errors or invalid references. This means if you accidentally save an invalid theme you don't have to worry that your app will blow up.\n\nTo stop watching the file, you can call `stop()` on the `FileWatcher` that is returned.\n\n\u003e Note that if a style property was present and you then remove it, Stylist cannot revert the change so that property will remain in the previous state.\n\n## 🎨 Theme\n\nA Theme file has a list of `variables` and a list of `styles`.\nVariables can be referenced in styles using `$variableName`.\n\nStyles are defined by [selector](#selectors), and are a map of [properties](Docs/StyleProperties.MD#properties) to [values](Docs/StyleProperties.MD#types)\n\n\n```yml\nvariables:\n  primaryColor: \"DB3B3B\"\nstyles:\n  primary:\n    color: $primaryColor\n```\n\n### Style Selectors\nStyles are defined using one or more selectors. Selectors can be a class or a style name or both. Custom classes must be prefixed by the module name. Style names must start with a lowercase.\n\nFor example:\n\n- `UIButton` all UIButtons\n- `MyApp.MyView` all MyView classes in the MyApp Module\n- `UITabBar.primary` all tab bars with the primary style\n- `primary` all styleables with the primary style\n\nThere can be multiple selectors separated by a space, which then check if the later selectors are contained in the earlier selectors. This only applies to UIViews and UIViewControllers. The containers don't have to be direct superviews but can be further up the responder chain.\n\nFor example, the following style will be applied to any `UIButton` that is contained within a view with a `section` style, that is within a `UIStackView` with the `main` style, and then within a `UINavigationController`.\n\n```yaml\nstyles:\n  UINavigationController UIStackView.main section UIButton:\n    font: title3\n```\n\nStyles will be applied in order of specificity, so the more specific a style is (more selectors), the later it will be applied.\n\n### Style references\n\nEach style may also have a `styles` array that is an array of other inherited styles, who's properties will also be applied without overwriting anything.\n\n```yml\nstyles:\n  primary:\n    styles: [themed]\n  themed:\n    tintColor: red\n    backgroundColor: EEEEEE\n```\n\n### View hierarchy styles\n\nStyles can reference the view hierarchy and then style that with its own properties. This is really useful for testing or accessing parts of the view hierarchy easily (`UIViewController.view` for example)\n\nThe sub styles are available on the following types:\n\n- **UIView**\n\t- `superview`: The superview\n\t- `next`: The next sibling view\n\t- `previous`: The previous sibling view\n\t- `viewController`: The view controller the view belongs to\n- **UIViewController**\n\t- `view`: The root view\n\t- `parent`: The parent view controller\n\t- `navigationController`: The UINavigationController this is contained in\n\t- `tabBarController`: The UITabBarController this is contained in\n\t- `tabBar`: The UITabBar for this view controller. Can be accessed on any child view controller\n\t- `navigationBar`: The UINavigationBar for this view controller. Can be accessed on any child view controller\n\n```yml\nstyles:\n  MyApp.MyViewController:\n    view:\n      tintColor: red\n    navigationBar:\n      tintColor: red\n```\n\n### Style Context\nStyle properties can be restricted to a certain context, for example a certain control state or trait collection. This is similar to how CSS media queries work. See [Context](Docs/StyleProperties.MD#context) for more info\n\n```yml\nstyles:\n  UIButton.primary:\n    backgroundImage: buttonBack\n    backgroundImage:highlighted: buttonBack-highlighted\n  UIStackView.main:\n    axis(horizontal:regular): horizontal\n    axis(horizontal:compact): vertical\n  title:\n    font(device:iphone): $headingFont:16\n    font(device:ipad): $headingFont:22\n```\n\n## 🖍 Style Properties\nMany UIKit views and bar buttons have built in properties that you can set. These can be viewed in [Style Properties](Docs/StyleProperties.MD).\n\n## ⚙️ Custom Properties\nCustom properties and parsers can also be added to let you configure anything you wish in a strongly typed way.\n\nTo create a `StyleProperty` pass a name and a generic closure that sets the property. Make sure to provide types for the styleable class and the generic `PropertyValue`.\n\n```swift\n// creates a new property that is applies a TextTransform to a MyLabel\n// access the property context and value via the PropertyValue\nlet property = StyleProperty(name: \"textTransform\") { (view: MyLabel, value: PropertyValue\u003cTextTransform\u003e) in\n    view.textTransform = value.value\n}\n\n// adds the custom property to Stylist\nStylist.shared.addProperty(property)\n```\n\nThe value must conform to `StyleValue` which is a simple protocol:\n\n```swift\npublic protocol StyleValue {\n    associatedtype ParsedType\n    static func parse(value: Any) -\u003e ParsedType?\n}\n```\n\nThe `PropertyValue` will have a `value` property containing your parsed value. It also has a `context` which contains the [property context](Docs/StyleProperties.MD#context) like device type, UIControlState, UIBarMetrics, size classes..etc.\n\nWhen a theme is loaded or when a style is set on a view, these custom properties will be applied if the view type and property name match.\n\nMany different types of properties are already supported and listed here in [Style Property Types](Docs/StyleProperties.MD#types)\n\n## ⚙️ Custom Styleable class\nBy default `UIView`, `UIViewController` and `UIBarItem` are styleable. You can make any custom class styleable as well by conforming to the `Styleable` protocol.\nThe inbuilt `Styleable` classes automatically call `applyStyles`, so you will have to do that automatically in your `styles` setter.\n\n```swift\npublic protocol Styleable: class {\n    var styles: [String] { get set }\n}\n\nextension Styleable {\n\n    func applyStyles() {\n        Stylist.shared.style(self)\n    }\n}\n```\n\n## 👥 Attributions\n\nThis tool is powered by:\n\n- [KZFileWatchers](https://github.com/krzysztofzablocki/KZFileWatchers)\n- [Yams](https://github.com/jpsim/Yams)\n\n## 👤 Contributions\nPull requests and issues are welcome\n\n## 📄 License\n\nStylist is licensed under the MIT license. See [LICENSE](LICENSE) for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyonaskolb%2Fstylist","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyonaskolb%2Fstylist","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyonaskolb%2Fstylist/lists"}