{"id":13534349,"url":"https://github.com/playcanvas/pcui","last_synced_at":"2026-03-01T13:04:25.291Z","repository":{"id":38085816,"uuid":"286531410","full_name":"playcanvas/pcui","owner":"playcanvas","description":"UI component library for web-based tools","archived":false,"fork":false,"pushed_at":"2026-02-04T20:17:15.000Z","size":9645,"stargazers_count":744,"open_issues_count":25,"forks_count":76,"subscribers_count":16,"default_branch":"main","last_synced_at":"2026-02-05T08:15:52.655Z","etag":null,"topics":["pcui","playcanvas","react","sass","typescript","ui","ui-components"],"latest_commit_sha":null,"homepage":"https://developer.playcanvas.com/user-manual/pcui/","language":"TypeScript","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/playcanvas.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-08-10T16:55:02.000Z","updated_at":"2026-02-04T20:01:45.000Z","dependencies_parsed_at":"2022-07-12T17:12:02.257Z","dependency_job_id":"cd930f43-5d00-4b81-8437-279db32401f5","html_url":"https://github.com/playcanvas/pcui","commit_stats":{"total_commits":410,"total_committers":21,"mean_commits":"19.523809523809526","dds":0.578048780487805,"last_synced_commit":"1d2260815dd5b4c5616c7119f069c21aec01973f"},"previous_names":[],"tags_count":96,"template":false,"template_full_name":null,"purl":"pkg:github/playcanvas/pcui","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/playcanvas%2Fpcui","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/playcanvas%2Fpcui/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/playcanvas%2Fpcui/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/playcanvas%2Fpcui/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/playcanvas","download_url":"https://codeload.github.com/playcanvas/pcui/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/playcanvas%2Fpcui/sbom","scorecard":{"id":737410,"data":{"date":"2025-08-11","repo":{"name":"github.com/playcanvas/pcui","commit":"245c6ddb920590f487ba3f64f5a563a2d18df009"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.7,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":1,"reason":"Found 3/18 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":5,"reason":"6 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 5","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'actions' permission set to 'read': .github/workflows/codeql.yml:28","Info: jobLevel 'contents' permission set to 'read': .github/workflows/codeql.yml:29","Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Warn: no topLevel permission defined: .github/workflows/codeql.yml:1","Info: topLevel 'contents' permission set to 'read': .github/workflows/deploy.yml:9","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:69: update your workflow using https://app.stepsecurity.io/secureworkflow/playcanvas/pcui/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:72: update your workflow using https://app.stepsecurity.io/secureworkflow/playcanvas/pcui/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:90: update your workflow using https://app.stepsecurity.io/secureworkflow/playcanvas/pcui/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:93: update your workflow using https://app.stepsecurity.io/secureworkflow/playcanvas/pcui/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/playcanvas/pcui/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/playcanvas/pcui/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:45: update your workflow using https://app.stepsecurity.io/secureworkflow/playcanvas/pcui/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:48: update your workflow using https://app.stepsecurity.io/secureworkflow/playcanvas/pcui/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:43: update your workflow using https://app.stepsecurity.io/secureworkflow/playcanvas/pcui/codeql.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:47: update your workflow using https://app.stepsecurity.io/secureworkflow/playcanvas/pcui/codeql.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:61: update your workflow using https://app.stepsecurity.io/secureworkflow/playcanvas/pcui/codeql.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:74: update your workflow using https://app.stepsecurity.io/secureworkflow/playcanvas/pcui/codeql.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/deploy.yml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/playcanvas/pcui/deploy.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/deploy.yml:29: update your workflow using https://app.stepsecurity.io/secureworkflow/playcanvas/pcui/deploy.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/deploy.yml:43: update your workflow using https://app.stepsecurity.io/secureworkflow/playcanvas/pcui/deploy.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/deploy.yml:46: update your workflow using https://app.stepsecurity.io/secureworkflow/playcanvas/pcui/deploy.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/deploy.yml:52: update your workflow using https://app.stepsecurity.io/secureworkflow/playcanvas/pcui/deploy.yml/main?enable=pin","Info:   0 out of  17 GitHub-owned GitHubAction dependencies pinned","Info:   1 out of   1 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":10,"reason":"SAST tool is run on all commits","details":["Info: SAST configuration detected: CodeQL","Info: all commits (19) are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":6,"reason":"4 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-xffm-g5w8-qvg7","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-76c9-3jph-rj3q","Warn: Project is vulnerable to: GHSA-8cj5-5rvv-wf4v"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-22T16:17:37.888Z","repository_id":38085816,"created_at":"2025-08-22T16:17:37.888Z","updated_at":"2025-08-22T16:17:37.888Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29867152,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-26T16:38:37.846Z","status":"ssl_error","status_checked_at":"2026-02-26T16:37:58.932Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["pcui","playcanvas","react","sass","typescript","ui","ui-components"],"created_at":"2024-08-01T07:01:31.025Z","updated_at":"2026-02-26T20:12:35.613Z","avatar_url":"https://github.com/playcanvas.png","language":"TypeScript","readme":"# PlayCanvas UI (PCUI) - User Interface Component Library for the Web\n\n[![NPM Version](https://img.shields.io/npm/v/@playcanvas/pcui)](https://www.npmjs.com/package/@playcanvas/pcui)\n[![NPM Downloads](https://img.shields.io/npm/dw/@playcanvas/pcui)](https://npmtrends.com/@playcanvas/pcui)\n[![License](https://img.shields.io/npm/l/@playcanvas/pcui)](https://github.com/playcanvas/pcui/blob/main/LICENSE)\n[![Discord](https://img.shields.io/badge/Discord-5865F2?style=flat\u0026logo=discord\u0026logoColor=white\u0026color=black)](https://discord.gg/RSaMRzg)\n[![Reddit](https://img.shields.io/badge/Reddit-FF4500?style=flat\u0026logo=reddit\u0026logoColor=white\u0026color=black)](https://www.reddit.com/r/PlayCanvas)\n[![X](https://img.shields.io/badge/X-000000?style=flat\u0026logo=x\u0026logoColor=white\u0026color=black)](https://x.com/intent/follow?screen_name=playcanvas)\n\n| [User Manual](https://developer.playcanvas.com/user-manual/pcui) | [API Reference](https://api.playcanvas.com/pcui) | [ESM Examples](https://playcanvas.github.io/pcui/examples/) | [React Examples](https://playcanvas.github.io/pcui/storybook/) | [Blog](https://blog.playcanvas.com) | [Forum](https://forum.playcanvas.com) |\n\nThis library enables the creation of reliable and visually pleasing user interfaces by providing fully styled components that you can use directly on your site. The components are useful in a wide range of use cases, from creating simple forms to building graphical user interfaces for complex web tools.\n\n![PCUI Banner](https://forum-files-playcanvas-com.s3.dualstack.eu-west-1.amazonaws.com/original/2X/7/7e51de8ae69fa499dcad292efd21d7722dcf2dbd.jpeg)\n\nA full guide to using the PCUI library can be found [here](https://developer.playcanvas.com/user-manual/pcui/).\n\n\u003c/div\u003e\n\n## Getting Started\n\nTo install the PCUI NPM module, run the following command:\n\n    npm install @playcanvas/pcui --save-dev\n\nYou can then import each individual element from PCUI. In the example below, you can see how the PCUI `Label` component is imported from the PCUI library. The styles for PCUI are then imported into the example. Styles only need to be imported once per project.\n\n```javascript\nimport { Label } from '@playcanvas/pcui';\nimport '@playcanvas/pcui/styles';\n\nconst label = new Label({\n    text: 'Hello World'\n});\ndocument.body.appendChild(label.dom);\n```\n\nIf you'd like to include PCUI in your React project, you can import the individual components as follows:\n\n```javascript\nimport * as React from 'react';\nimport ReactDOM from 'react-dom';\nimport { TextInput } from '@playcanvas/pcui/react';\nimport '@playcanvas/pcui/styles';\n\nReactDOM.render(\n    \u003cTextInput text='Hello World'/\u003e,\n    document.body\n);\n```\n\n## Building a UMD bundle\n\nIf you need a UMD version of the PCUI library (say, to use it in a PlayCanvas Editor project), please refer to our [build guide](BUILDGUIDE.md).\n\n## Fonts in PCUI\n\nPCUI uses four CSS classes for fonts across its components: `.font-regular`, `.font-bold`, `.font-thin` and `.font-light`. By default, these use the Helvetica Neue font stack:\n\n```css\n    font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;\n```\n\n### Using your own Font\n\nYou can override PCUI's default font by adding your own `font-family` CSS rules to these classes on your webpage:\n\n```css\n.font-regular, .font-bold, .font-thin, .font-light {\n    font-family: 'Your Font', sans-serif;\n}\n```\n\n## Data Binding\n\nThe PCUI library offers a data binding layer that can be used to synchronize data across multiple components. It offers two way binding to a given observer object, so updates made in a component are reflected in the observer's data and distributed out to all other subscribed components. A simple use case is shown below:\n\n```javascript\nimport { Observer } from '@playcanvas/observer';\nimport { Label, TextInput, BindingObserversToElement, BindingElementToObservers } from '@playcanvas/pcui';\nimport '@playcanvas/pcui/styles';\n\n// create a new observer for a simple object which contains a text string\nconst observer = new Observer({\n    text: 'Hello World'\n});\n\n// create a label which will listen to updates from the observer\nconst label = new Label({\n    binding: new BindingObserversToElement()\n});\n\n// link the observer to the label, telling it to use the text variable as its value\nlabel.link(observer, 'text');\n\n// create a text input which will send updates to the observer\nconst textInput = new TextInput({\n    binding: new BindingElementToObservers()\n});\n\n// link the observer to the label, telling it to set the text variable on change\ntextInput.link(observer, 'text');\n```\n\nIn the above example, the created label will start with `Hello World` as its text value. When a user enters a value into the text input, the label will be updated with the new value.\n\nObservers can also be bound bi-directionally, in which case an element can both send and receive updates through its observer. The following example shows a two way binding between two text inputs, where either input can update the value of the other. It's been written in React to showcase binding with React components:\n\n```jsx\nimport { Observer } from '@playcanvas/observer';\nimport { BindingTwoWay, TextInput } from '@playcanvas/pcui/react';\nimport '@playcanvas/pcui/styles';\n\n// create a new observer for a simple object which contains a text string\nconst observer = new Observer({\n    text: 'Hello World'\n});\n\n// create two text inputs, which can both send and receive updates through the linked observer\nconst TextInput1 = () =\u003e \u003cTextInput binding={new BindingTwoWay()} link={{ observer, path: 'text'} /\u003e;\nconst TextInput2 = () =\u003e \u003cTextInput binding={new BindingTwoWay()} link={{ observer, path: 'text'} /\u003e;\n```\n\n## Development\n\nEach component exists in its own folder within the `./src/components` directory. They each contain:\n\n- `index.ts`: The PCUI element.\n- `style.scss`: The SASS styles for the PCUI element.\n- `component.tsx`: A React component wrapping the PCUI element.\n- `component.stories.tsx`: The Storybook entry for the React component.\n\nLocally developed components can be viewed \u0026 tested by running the Storybook app, as mentioned in the following section.\n\nIf you'd like to build your own custom version of the library you can run the `npm run build` command which will create a `dist` directory with your custom build.\n\n## Storybook\n\nIf you wish to view all components available to you in the library, you can run a local version Storybook. It allows you to browse the entire collection of components and test any changes you make to them. Each component page also includes component documentation and allows you to test each component in all of its configuration states.\n\nRun Storybook as follows:\n\n    npm install\n    npm run storybook\n\n## API Documentation\n\nTo build the PCUI API Reference to the `docs` folder, do:\n\n```bash\nnpm run docs\n```\n\n[resolution-badge]: https://isitmaintained.com/badge/resolution/playcanvas/pcui.svg\n[open-issues-badge]: https://isitmaintained.com/badge/open/playcanvas/pcui.svg\n[isitmaintained-url]: https://isitmaintained.com/project/playcanvas/pcui\n[npm-version-badge]: https://img.shields.io/npm/v/@playcanvas/pcui.svg?style=flat?style=flat\n[npm-package-url]: https://www.npmjs.com/package/@playcanvas/pcui\n[npm-downloads-badge]: https://img.shields.io/npm/dw/@playcanvas/pcui\n[npm-downloads-url]: https://npmtrends.com/@playcanvas/pcui\n[github-license-badge]: https://img.shields.io/badge/license-MIT-blue.svg\n[github-license-url]: https://github.com/playcanvas/pcui/blob/main/LICENSE\n[twitter-badge]: https://img.shields.io/twitter/follow/playcanvas.svg?style=social\u0026label=Follow\n[twitter-url]: https://twitter.com/intent/follow?screen_name=playcanvas","funding_links":[],"categories":["Libraries","TypeScript","Recently Updated","PlayCanvas Products"],"sub_categories":["JavaScript","[Feb 23, 2025](/content/2025/02/23/README.md)"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplaycanvas%2Fpcui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fplaycanvas%2Fpcui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplaycanvas%2Fpcui/lists"}