{"id":13428147,"url":"https://github.com/airbnb/paris","last_synced_at":"2025-05-15T04:04:21.766Z","repository":{"id":38428316,"uuid":"114683464","full_name":"airbnb/paris","owner":"airbnb","description":"Define and apply styles to Android views programmatically","archived":false,"fork":false,"pushed_at":"2023-05-25T02:03:11.000Z","size":28165,"stargazers_count":1913,"open_issues_count":56,"forks_count":86,"subscribers_count":24,"default_branch":"master","last_synced_at":"2025-04-14T04:59:06.639Z","etag":null,"topics":["android","android-ui","design-systems","kotlin","styles"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/airbnb.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2017-12-18T20:13:09.000Z","updated_at":"2025-03-19T12:51:30.000Z","dependencies_parsed_at":"2022-07-12T15:30:55.211Z","dependency_job_id":"e3c3700f-ae82-4451-9e48-1bdac49edf5f","html_url":"https://github.com/airbnb/paris","commit_stats":{"total_commits":433,"total_committers":21,"mean_commits":20.61904761904762,"dds":"0.17090069284064668","last_synced_commit":"d8b5edbc56253bcdd0d0c57930d2e91113dd0f37"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/airbnb%2Fparis","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/airbnb%2Fparis/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/airbnb%2Fparis/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/airbnb%2Fparis/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/airbnb","download_url":"https://codeload.github.com/airbnb/paris/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254270640,"owners_count":22042858,"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":["android","android-ui","design-systems","kotlin","styles"],"created_at":"2024-07-31T01:00:47.362Z","updated_at":"2025-05-15T04:04:21.737Z","avatar_url":"https://github.com/airbnb.png","language":"Kotlin","funding_links":[],"categories":["Libraries","Kotlin"],"sub_categories":[],"readme":"# Paris\nParis lets you define and apply styles programmatically to Android views, including custom attributes.\n\n* Apply styles programmatically at any time.\n* Combine multiple styles together.\n* Create styles programmatically (as opposed to using XML).\n* Use annotations to easily support custom attributes (inspired by [Barber](https://github.com/hzsweers/barber)).\n* Declare explicitly supported styles for your custom views.\n* And much more...\n\n## Installation\n\nIn your project's `build.gradle`:\n```gradle\ndependencies {\n    implementation 'com.airbnb.android:paris:2.0.0'\n    // Apply the Paris processor if you're using Paris annotations for code gen.\n    kapt 'com.airbnb.android:paris-processor:2.0.0'\n    // or if you are using Kotlin Symbol Processing\n    ksp 'com.airbnb.android:paris-processor:2.0.0'\n}\n```\n\nTo use Paris in a library module see [Library Modules](../../wiki/Library-Modules).\n\n## Quick Start\n\n### Applying an XML-Defined Style\n\n```kotlin\nmyView.style(R.style.MyStyle)\n```\n\u003cdetails\u003e\u003csummary\u003eClick to see the example in Java.\u003c/summary\u003e\n\n```java\nParis.style(myView).apply(R.style.MyStyle);\n```\n\u003c/details\u003e\u003cbr/\u003e\n\nWhere `myView` is an arbitrary view instance, `MyStyle` an XML-defined style, and `style` an extension function provided by Paris. Many but not all attributes are supported, for more see [Supported View Types and Attributes](../../wiki/Supported-View-Types-and-Attributes).\n\n### Combining 2 or More Styles\n\n```kotlin\nmyView.style {\n    add(R.style.StyleA)\n    add(R.style.StyleB)\n    …\n}\n```\n\u003cdetails\u003e\u003csummary\u003eClick to see the example in Java.\u003c/summary\u003e\n\n```java\nParis.styleBuilder(myView)\n        .add(R.style.StyleA)\n        .add(R.style.StyleB)\n        …\n        .apply();\n```\n\u003c/details\u003e\u003cbr/\u003e\n\nIn cases where there's some overlap the attribute value from the last style added prevails. For more see [Combining Styles](../../wiki/Building-and-Applying-Styles#combining-styles).\n\n### Defining Styles Programmatically\n\n```kotlin\ntextView.style {\n    // Using an actual value.\n    textColor(Color.GREEN)\n    // Or a resource.\n    textSizeRes(R.dimen.my_text_size_small)\n}\n```\n\u003cdetails\u003e\u003csummary\u003eClick to see the example in Java.\u003c/summary\u003e\n\n```java\nParis.styleBuilder(textView)\n        // Using an actual value.\n        .textColor(Color.GREEN)\n        // Or a resource.\n        .textSizeRes(R.dimen.my_text_size_small)\n        .apply();\n```\n\u003c/details\u003e\u003cbr/\u003e\n\nCan be combined with style resources as well:\n```kotlin\ntextView.style {\n    // Adds all the attributes defined in the MyGreenTextView style.\n    add(R.style.MyGreenTextView)\n    textSizeRes(R.dimen.my_text_size_small)\n}\n```\n\u003cdetails\u003e\u003csummary\u003eClick to see the example in Java.\u003c/summary\u003e\n\n```java\nParis.styleBuilder(textView)\n        // Adds all the attributes defined in the MyGreenTextView style.\n        .add(R.style.MyGreenTextView)\n        .textSizeRes(R.dimen.my_text_size_small)\n        .apply();\n```\n\u003c/details\u003e\u003cbr/\u003e\n\nFor more see [Defining Styles Programmatically](../../wiki/Building-and-Applying-Styles#defining-styles-programmatically).\n\n### Custom View Attributes\n\nAttributes are declared as followed:\n```xml\n\u003cdeclare-styleable name=\"MyView\"\u003e\n    \u003cattr name=\"title\" format=\"string\" /\u003e\n    \u003cattr name=\"image\" format=\"reference\" /\u003e\n    \u003cattr name=\"imageSize\" format=\"dimension\" /\u003e\n\u003c/declare-styleable\u003e\n```\n\nThe custom view is annotated with `@Styleable` and `@Attr`:\n```kotlin\n// The value here corresponds to the name chosen in declare-styleable.\n@Styleable(\"MyView\")\nclass MyView(…) : ViewGroup(…) {\n\n    init {\n        // This call enables the custom attributes when used in XML layouts. It\n        // extracts styling information from AttributeSet like it would a StyleRes.\n        style(attrs)\n    }\n\n    @Attr(R.styleable.MyView_title)\n    fun setTitle(title: String) {\n        // Automatically called with the title value (if any) when an AttributeSet\n        // or StyleRes is applied to the MyView instance.\n    }\n\n    @Attr(R.styleable.MyView_image)\n    fun setImage(image: Drawable?) {\n        // Automatically called with the image value (if any) when an AttributeSet\n        // or StyleRes is applied to the MyView instance.\n    }\n\n    @Attr(R.styleable.MyView_imageSize)\n    fun setImageSize(@Px imageSize: Int) {\n        // Automatically called with the imageSize value (if any) when an\n        // AttributeSet or StyleRes is applied to the MyView instance.\n    }\n}\n```\n\u003cdetails\u003e\u003csummary\u003eClick to see the example in Java.\u003c/summary\u003e\n\n```java\n// The value here corresponds to the name chosen in declare-styleable.\n@Styleable(\"MyView\")\npublic class MyView extends ViewGroup {\n\n    public MyView(Context context) {\n        super(context);\n    }\n\n    public MyView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public MyView(Context context, AttributeSet attrs, int defStyle) {\n        this(context, attrs, defStyle);\n        // This call enables the custom attributes when used in XML layouts. It\n        // extracts styling information from AttributeSet like it would a StyleRes.\n        Paris.style(this).apply(attrs);\n    }\n\n    @Attr(R.styleable.MyView_title)\n    public void setTitle(String title) {\n        // Automatically called with the title value (if any) when an AttributeSet\n        // or StyleRes is applied to the MyView instance.\n    }\n\n    @Attr(R.styleable.MyView_image)\n    public void setImage(Drawable image) {\n        // Automatically called with the image value (if any) when an AttributeSet\n        // or StyleRes is applied to the MyView instance.\n    }\n\n    @Attr(R.styleable.MyView_imageSize)\n    public void setImageSize(@Px int imageSize) {\n        // Automatically called with the imageSize value (if any) when an\n        // AttributeSet or StyleRes is applied to the MyView instance.\n    }\n}\n```\n\u003c/details\u003e\u003cbr/\u003e\n\nThe `@Attr`-annotated methods will be called by Paris when the view is inflated with an `AttributeSet` or when a style is applied.\n\nFor more see [Custom View Attributes](../../wiki/Custom-View-Attributes).\n\n### Styling Subviews\n\nAttributes are declared as followed for the 2 subviews we'd like to be able to style:\n```xml\n\u003cdeclare-styleable name=\"MyHeader\"\u003e\n    \u003cattr name=\"titleStyle\" format=\"reference\" /\u003e\n    \u003cattr name=\"subtitleStyle\" format=\"reference\" /\u003e\n    ...\n\u003c/declare-styleable\u003e\n```\n\nThe subview fields are annotated with `@StyleableChild`:\n```kotlin\n@Styleable(\"MyHeader\")\nclass MyHeader(…) : ViewGroup(…) {\n\n    @StyleableChild(R.styleable.MyHeader_titleStyle)\n    internal val title: TextView …\n    \n    @StyleableChild(R.styleable.MyHeader_subtitleStyle)\n    internal val subtitle: TextView …\n    \n    init {\n        style(attrs)\n    }\n}\n```\n\u003cdetails\u003e\u003csummary\u003eClick to see the example in Java.\u003c/summary\u003e\n\n```java\n@Styleable(\"MyHeader\")\npublic class MyHeader extends ViewGroup {\n\n    @StyleableChild(R.styleable.MyHeader_titleStyle)\n    TextView title;\n    \n    @StyleableChild(R.styleable.MyHeader_subtitleStyle)\n    TextView subtitle;\n    \n    …\n    // Make sure to call Paris.style(this).apply(attrs) during initialization.\n}\n```\n\u003c/details\u003e\u003cbr/\u003e\n\nThe title and subtitle styles can now be part of `MyHeader` styles:\n```xml\n\u003cMyHeader\n    ...\n    app:titleStyle=\"@style/Title2\"\n    app:subtitleStyle=\"@style/Regular\" /\u003e\n```\n\n```kotlin\nmyHeader.style {\n    // Defined in XML.\n    titleStyle(R.style.Title2)\n    // Defined programmatically.\n    subtitleStyle {\n        textColorRes(R.color.text_color_regular)\n        textSizeRes(R.dimen.text_size_regular)\n    }\n}\n```\n\u003cdetails\u003e\u003csummary\u003eClick to see the example in Java.\u003c/summary\u003e\n\n```java\nParis.styleBuilder(myHeader)\n        // Defined in XML.\n        .titleStyle(R.style.Title2)\n        // Defined programmatically.\n        .subtitleStyle((builder) -\u003e builder\n                .textColorRes(R.color.text_color_regular)\n                .textSizeRes(R.dimen.text_size_regular))\n        .apply();\n```\n\u003c/details\u003e\u003cbr/\u003e\n\n**Attention:** Extension functions like `titleStyle` and `subtitleStyle` are generated during compilation by the Paris annotation processor. When new `@StyleableChild` annotations are added, the project must be (re)compiled once for the related functions to become available.\n\nFor more see [Styling Subviews](../../wiki/Custom-Views#styling-subviews).\n\n### Linking Styles to Views\n\n```kotlin\n@Styleable\nclass MyView(…) : View(…) {\n\n    companion object {\n        // For styles defined in XML.\n        @Style\n        val RED_STYLE = R.style.MyView_Red\n\n        // For styles defined programmatically.\n        @Style\n        val GREEN_STYLE = myViewStyle {\n            background(R.color.green)\n        }\n    }\n}\n```\n\u003cdetails\u003e\u003csummary\u003eClick to see the example in Java.\u003c/summary\u003e\n\n```java\n@Styleable\npublic class MyView extends View {\n\n    // For styles defined in XML.\n    @Style\n    static final int RED_STYLE = R.style.MyView_Red;\n\n    // For styles defined programmatically.\n    @Style\n    static void greenStyle(MyViewStyleApplier.StyleBuilder builder) {\n        builder.background(R.color.green);\n    }\n}\n```\n\u003c/details\u003e\u003cbr/\u003e\n\nHelper methods are generated for each linked style:\n```kotlin\nmyView.style { addRed() } // Equivalent to style(R.style.MyView_Red)\nmyView.style { addGreen() } // Equivalent to add(MyView.GREEN_STYLE)\n\nmyView.style {\n    addRed() // Equivalent to add(R.style.MyView_Red)\n    addGreen() // Equivalent to add(MyView.GREEN_STYLE)\n    …\n}\n```\n\u003cdetails\u003e\u003csummary\u003eClick to see the example in Java.\u003c/summary\u003e\n\n```java\nParis.style(myView).applyRed(); // Equivalent to apply(R.style.MyView_Red)\nParis.style(myView).applyGreen(); // No equivalent.\n\nParis.styleBuilder(myView)\n        .addRed() // Equivalent to add(R.style.MyView_Red)\n        .addGreen() // No equivalent.\n        …\n        .apply();\n```\n\u003c/details\u003e\u003cbr/\u003e\n\n**Attention:** Extension functions like `addRed` and `addGreen` are generated during compilation by the Paris annotation processor. When new `@Style` annotations are added, the project must be (re)compiled once for the related functions to become available.\n\nFor more see [Linking Styles to Custom Views](../../wiki/Custom-Views#linking-styles-to-custom-views).\n\n## Documentation\n\nSee examples and browse complete documentation at the [Paris Wiki](../../wiki).\n\nIf you still have questions, feel free to create a new issue.\n\n## Contributing\n\nWe love contributions! Check out our [contributing guidelines](CONTRIBUTING.md) and be sure to follow our [code of conduct](CODE_OF_CONDUCT.md).\n\n## License\n\n```\nCopyright 2018 Airbnb, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fairbnb%2Fparis","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fairbnb%2Fparis","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fairbnb%2Fparis/lists"}