├── .changeset
├── README.md
└── config.json
├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.yml
│ ├── config.yml
│ └── feature_request.yml
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ └── release.yml
├── .gitignore
├── .npmrc
├── .nvmrc
├── .prettierignore
├── .prettierrc
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── lefthook.yml
├── manifest.json
├── package-lock.json
├── package.json
├── postcss.config.js
├── rollup.config.mjs
├── src
├── App.svelte
├── app.css
├── app.js
├── img
│ ├── Icon.png
│ ├── coverArt.png
│ ├── jewel.svg
│ └── wordmark.svg
├── index.html
├── lib
│ ├── components
│ │ ├── Controls
│ │ │ ├── File.svelte
│ │ │ ├── Images.svelte
│ │ │ ├── Page.svelte
│ │ │ ├── Preview.svelte
│ │ │ ├── Text.svelte
│ │ │ └── Variables.svelte
│ │ ├── DatafaceLogo.svelte
│ │ ├── ErrorMessage.svelte
│ │ ├── Footer.svelte
│ │ ├── Inputs
│ │ │ ├── Button.svelte
│ │ │ ├── Checkbox.svelte
│ │ │ ├── Input.svelte
│ │ │ ├── Select.svelte
│ │ │ └── TextArea.svelte
│ │ ├── Layout
│ │ │ ├── Panel.svelte
│ │ │ └── PreviewCard.svelte
│ │ ├── Overlay.svelte
│ │ └── WindowResize.svelte
│ ├── generator
│ │ ├── convertTextFrames.ts
│ │ ├── createSettingsBlock.ts
│ │ ├── css
│ │ │ ├── frame.ts
│ │ │ ├── index.ts
│ │ │ ├── page.ts
│ │ │ └── textEffect.ts
│ │ ├── group.ts
│ │ ├── html
│ │ │ ├── frame.ts
│ │ │ ├── index.ts
│ │ │ ├── span.ts
│ │ │ └── wrapper.ts
│ │ ├── js
│ │ │ ├── fonts.ts
│ │ │ ├── index.ts
│ │ │ └── resizer.ts
│ │ ├── styleProps.ts
│ │ └── widthRange.ts
│ ├── pkg.js
│ └── utils
│ │ ├── autotype.ts
│ │ ├── camelize.ts
│ │ ├── dashify.ts
│ │ ├── extractTextFromHTML.ts
│ │ ├── isFigma2htmlFrame.ts
│ │ ├── isNodeVisible.ts
│ │ ├── log.ts
│ │ ├── roundTo.ts
│ │ ├── slugify.ts
│ │ ├── stringify.ts
│ │ ├── textNodeVariable.ts
│ │ ├── timestamp.ts
│ │ ├── trim.ts
│ │ └── zeroPad.ts
├── main.ts
└── types.d.ts
├── tailwind.config.js
└── tsconfig.json
/.changeset/README.md:
--------------------------------------------------------------------------------
1 | # Changesets
2 |
3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4 | with multi-package repos, or single-package repos to help you version and publish your code. You can
5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6 |
7 | We have a quick list of common questions to get you started engaging with this project in
8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
9 |
--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@1.5.0/schema.json",
3 | "changelog": [
4 | "@svitejs/changesets-changelog-github-compact",
5 | { "repo": "the-dataface/figma2html" }
6 | ],
7 | "commit": false,
8 | "fixed": [],
9 | "linked": [],
10 | "access": "restricted",
11 | "privatePackages": { "version": true, "tag": true },
12 | "baseBranch": "main",
13 | "updateInternalDependencies": "patch",
14 | "ignore": []
15 | }
16 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = tab
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | .github
2 | .vscode
3 | node_modules
4 | dist
5 | build
6 | coverage
7 | .md
8 | *.d.ts
9 | !**/*.{ts,tsx,js,jsx,svelte}
10 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | // https://github.com/sveltejs/eslint-plugin-svelte3
2 | {
3 | "root": true,
4 | "parser": "@typescript-eslint/parser",
5 | "extends": ["eslint:recommended", "prettier"],
6 | "plugins": ["svelte3", "@typescript-eslint"],
7 | "overrides": [{ "files": ["*.svelte"], "processor": "svelte3/svelte3" }],
8 | "parserOptions": {
9 | "sourceType": "module",
10 | "ecmaVersion": 2020
11 | },
12 | "env": {
13 | "browser": true,
14 | "es6": true,
15 | "node": true
16 | },
17 | "rules": {
18 | "no-undef": "off"
19 | },
20 | "settings": {
21 | "svelte3/typescript": true
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: "\U0001F6A8 Bug report"
2 | description: Report an issue with figma2html
3 | labels: ['bug']
4 | body:
5 | - type: markdown
6 | attributes:
7 | value: |
8 | Thanks for taking the time to fill out this bug report!
9 | - type: textarea
10 | id: bug-description
11 | attributes:
12 | label: Describe the bug
13 | description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us in the description. Thanks!
14 | placeholder: Bug description
15 | validations:
16 | required: true
17 | - type: textarea
18 | id: reproduction
19 | attributes:
20 | label: Reproduction
21 | description: Please provide a link to a repo or Figma file that can reproduce the problem you ran into. If a report is vague (e.g. just a generic error message) and has no reproduction, it will receive a "need reproduction" label. If no reproduction is provided within a reasonable time-frame, the issue will be closed.
22 | placeholder: Reproduction
23 | validations:
24 | required: true
25 | - type: textarea
26 | id: logs
27 | attributes:
28 | label: Logs
29 | description: 'Please include browser console and server logs around the time this bug occurred. Optional if provided reproduction. Please try not to insert an image but copy paste the log text.'
30 | render: Shell
31 | - type: textarea
32 | id: system-info
33 | attributes:
34 | label: System Info
35 | description: Figma version, OS version, Browser version, etc.
36 | render: Shell
37 | placeholder: System, Binaries, Browsers
38 | validations:
39 | required: true
40 | - type: dropdown
41 | id: severity
42 | attributes:
43 | label: Severity
44 | description: Select the severity of this issue
45 | options:
46 | - annoyance
47 | - blocking an upgrade
48 | - blocking all usage of figma2html
49 | validations:
50 | required: true
51 | - type: checkboxes
52 | id: terms
53 | attributes:
54 | label: Code of Conduct
55 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/the-dataface/figma2html/blob/main/CODE_OF_CONDUCT.md)
56 | options:
57 | - label: I agree to follow figma2html's Code of Conduct
58 | required: true
59 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yml:
--------------------------------------------------------------------------------
1 | name: "\U0001F680 Feature Request"
2 | description: Request a new figma2html feature
3 | labels: [enhancement]
4 | body:
5 | - type: markdown
6 | attributes:
7 | value: |
8 | Thanks for taking the time to request this feature! A maintainer will review your request and respond as soon as possible. Feel free to open a PR if you'd like to implement this feature yourself.
9 | - type: textarea
10 | id: problem
11 | attributes:
12 | label: Describe the problem
13 | description: Please provide a clear and concise description the problem this feature would solve. The more information you can provide here, the better.
14 | placeholder: I'm always frustrated when...
15 | validations:
16 | required: true
17 | - type: textarea
18 | id: solution
19 | attributes:
20 | label: Describe the proposed solution
21 | description: Please provide a clear and concise description of what you would like to happen.
22 | placeholder: I would like to see...
23 | validations:
24 | required: true
25 | - type: textarea
26 | id: alternatives
27 | attributes:
28 | label: Alternatives considered
29 | description: "Please provide a clear and concise description of any alternative solutions or features you've considered."
30 | validations:
31 | required: true
32 | - type: dropdown
33 | id: importance
34 | attributes:
35 | label: Importance
36 | description: How important is this feature to you?
37 | options:
38 | - nice to have
39 | - would make my life easier
40 | - i cannot use figma2html without it
41 | validations:
42 | required: true
43 | - type: checkboxes
44 | id: terms
45 | attributes:
46 | label: Code of Conduct
47 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/the-dataface/figma2html/blob/main/CODE_OF_CONDUCT.md)
48 | options:
49 | - label: I agree to follow figma2html's Code of Conduct
50 | required: true
51 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### Before submitting the PR, please make sure you do the following
2 |
3 | - [ ] It's really useful if your PR references an issue where it is discussed ahead of time.
4 | - [ ] Prefix your PR title with `[feat]`, `[fix]`, `[chore]`, `[docs]` or another appropriate prefix from the [Contributing guide](https://github.com/the-dataface/figma2html/blob/main/CONTRIBUTING.md).
5 | - [ ] This message body should clearly illustrate what problems it solves.
6 | - [ ] Ideally, link a repo or Figma file that fails export without this PR but passes with it.
7 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | concurrency: ${{ github.workflow }}-${{ github.ref }}
9 |
10 | jobs:
11 | release:
12 | name: Release
13 | if: github.repository == 'the-dataface/figma2html'
14 | runs-on: ubuntu-latest
15 | steps:
16 | - name: Checkout Repo
17 | uses: actions/checkout@v3
18 |
19 | - name: Setup Node.js 16
20 | uses: actions/setup-node@v3
21 | with:
22 | node-version: 16.x
23 | cache: npm
24 |
25 | - name: Install Dependencies
26 | run: npm ci
27 |
28 | - name: Create Release Pull Request
29 | uses: changesets/action@v1
30 | env:
31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .vscode
3 | node_modules
4 | /build
5 | .env
6 | .env.*
7 | !.env.example
8 | package-lock.json
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | engine-strict=true
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | lts/gallium
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /build
4 | /.github
5 | /.githooks
6 | /.vscode
7 | .env
8 | .env.*
9 | !.env.example
10 | .d.ts
11 | .md
12 | lefthook.yml
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "useTabs": true,
3 | "singleQuote": true,
4 | "trailingComma": "none",
5 | "printWidth": 100,
6 | "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
7 | "pluginSearchDirs": false,
8 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
9 | }
10 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # figma2html
2 |
3 | ## 1.1.0
4 |
5 | ### Minor Changes
6 |
7 | - Support multi-line variable text ([#100](https://github.com/the-dataface/figma2html/pull/100))
8 |
9 | - - Alter `variables` workflows by adding UI panel and making entries async to config block ([#100](https://github.com/the-dataface/figma2html/pull/100))
10 | - Edit figma2html settings block, grouping together (migration is built in)
11 | - Update reset/load/save functionality
12 |
13 | ### Patch Changes
14 |
15 | - Fix f2h-variables frame duplication ([#100](https://github.com/the-dataface/figma2html/pull/100))
16 |
17 | - Fix Google Fonts family variant detection ([#111](https://github.com/the-dataface/figma2html/pull/111))
18 |
19 | ## 1.0.3
20 |
21 | ### Patch Changes
22 |
23 | - Fix custom `` ending tag ([#106](https://github.com/the-dataface/figma2html/pull/106))
24 |
25 | ## 1.0.3-beta
26 |
27 | ### Patch Changes
28 |
29 | - Notify users of malformatted YAML ([#101](https://github.com/the-dataface/figma2html/pull/101))
30 |
31 | - Remove duplicate variables frame + fix placement ([#101](https://github.com/the-dataface/figma2html/pull/101))
32 |
33 | ## 1.0.2-beta
34 |
35 | ### Patch Changes
36 |
37 | - Fix text node visibility issue by recursively checking parent visibility. See [Figma's visible docs](https://www.figma.com/plugin-docs/api/properties/nodes-visible/#remarks) for more info. Added a isNodeVisible utils to perform recursion and isFigma2htmlFrame to check if passed node is a valid figma2html frame. ([#98](https://github.com/the-dataface/figma2html/pull/98))
38 |
39 | ## 1.0.1-beta
40 |
41 | ### Patch Changes
42 |
43 | - Fix text styling #92. Text styles were broken during a prior refactor, causing `font-family` and `font-size` and other critical styles to not be assigned in the resulting files. ([#94](https://github.com/the-dataface/figma2html/pull/94))
44 |
45 | - - Frames that were acting as shapes (ie with fills and strokes) were being converted to groups and thus losing their styling. That’s addressed. ([#96](https://github.com/the-dataface/figma2html/pull/96))
46 | - Missing font family names and missing segments were causing an error in a specific debugging file. I defaulted those cases to ‘Inter Normal’; should be fixed.
47 | - `Apply header tags` feature has been removed in favor of a more robust attribute application. Now you can set custom attributes via a typical `key="value"` syntax, with `tag="TAGNAME"` reserved for changing the tag from `p` to the desired entry.
48 | - Fluid width was stretching images (#86). Fixed in #87 and reflected here, by setting image height to `null` when `Fluid container width` is turned on.
49 | - Text in components and component instances was not being recognized as text and instead was exporting as part of the image. I’ve made it so the `tempFrame` converts frames (that have text children), auto layouts, and components to groups and detaches instances, which means text nodes are have correct positioning, relative to the base frame.
50 | - We were still seeing invisible text nodes in the final export. I’ve updated `withModificationsForExport` to just remove those nodes from the `tempFrame`.
51 | - Text positioning was way off. Converting frames with text children to groups helped with part of that and correcting the artboard height and width fixed the rest... I hope?
52 |
53 | ## 1.0.1-beta
54 |
55 | ### Patch Changes
56 |
57 | - - Ignore empty text nodes. Previously would error when trying to access textNode.styles, which is false. I could not find a way for the Figma plugin API to ignore empty nodes by default so included safeguards in convertTextFrames.ts. ([#88](https://github.com/the-dataface/figma2html/pull/88))
58 | - Ensure textNode.textStyleId is not a symbol. Text nodes with mixed styles get a symbol as their style id. I added a conditional to ignore that ID and proceed as if none were found.
59 |
60 | ## 1.0.0-beta
61 |
62 | ### Major changes
63 |
64 | - Initial beta release ([#84](https://github.com/the-dataface/figma2html/pull/84))
65 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Figma2html Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, caste, color, religion, or sexual
10 | identity and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | - Demonstrating empathy and kindness toward other people
21 | - Being respectful of differing opinions, viewpoints, and experiences
22 | - Giving and gracefully accepting constructive feedback
23 | - Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | - Focusing on what is best not just for us as individuals, but for the overall
26 | community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | - The use of sexualized language or imagery, and sexual attention or advances of
31 | any kind
32 | - Trolling, insulting or derogatory comments, and personal or political attacks
33 | - Public or private harassment
34 | - Publishing others' private information, such as a physical or email address,
35 | without their explicit permission
36 | - Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at [sam@thedataface.com](mailto:sam@thedataface.com?subject=Figma2html%20-%20Code%20of%20Conduct) and [sawyer@thedataface.com](mailto:sawyer@thedataface.com?subject=Figma2html%20-%20Code%20of%20Conduct).
63 | All complaints will be reviewed and investigated promptly and fairly.
64 |
65 | All community leaders are obligated to respect the privacy and security of the
66 | reporter of any incident.
67 |
68 | ## Enforcement Guidelines
69 |
70 | Community leaders will follow these Community Impact Guidelines in determining
71 | the consequences for any action they deem in violation of this Code of Conduct:
72 |
73 | ### 1. Correction
74 |
75 | **Community Impact**: Use of inappropriate language or other behavior deemed
76 | unprofessional or unwelcome in the community.
77 |
78 | **Consequence**: A private, written warning from community leaders, providing
79 | clarity around the nature of the violation and an explanation of why the
80 | behavior was inappropriate. A public apology may be requested.
81 |
82 | ### 2. Warning
83 |
84 | **Community Impact**: A violation through a single incident or series of
85 | actions.
86 |
87 | **Consequence**: A warning with consequences for continued behavior. No
88 | interaction with the people involved, including unsolicited interaction with
89 | those enforcing the Code of Conduct, for a specified period of time. This
90 | includes avoiding interactions in community spaces as well as external channels
91 | like social media. Violating these terms may lead to a temporary or permanent
92 | ban.
93 |
94 | ### 3. Temporary Ban
95 |
96 | **Community Impact**: A serious violation of community standards, including
97 | sustained inappropriate behavior.
98 |
99 | **Consequence**: A temporary ban from any sort of interaction or public
100 | communication with the community for a specified period of time. No public or
101 | private interaction with the people involved, including unsolicited interaction
102 | with those enforcing the Code of Conduct, is allowed during this period.
103 | Violating these terms may lead to a permanent ban.
104 |
105 | ### 4. Permanent Ban
106 |
107 | **Community Impact**: Demonstrating a pattern of violation of community
108 | standards, including sustained inappropriate behavior, harassment of an
109 | individual, or aggression toward or disparagement of classes of individuals.
110 |
111 | **Consequence**: A permanent ban from any sort of public interaction within the
112 | community.
113 |
114 | ## Attribution
115 |
116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
117 | version 2.1, available at
118 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
119 |
120 | Community Impact Guidelines were inspired by
121 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
122 |
123 | For answers to common questions about this code of conduct, see the FAQ at
124 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
125 | [https://www.contributor-covenant.org/translations][translations].
126 |
127 | [homepage]: https://www.contributor-covenant.org
128 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
129 | [Mozilla CoC]: https://github.com/mozilla/diversity
130 | [FAQ]: https://www.contributor-covenant.org/faq
131 | [translations]: https://www.contributor-covenant.org/translations
132 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## Table of Contents
4 |
5 | - [Contributing](#contributing)
6 | - [Table of Contents](#table-of-contents)
7 | - [Requirements](#requirements)
8 | - [Getting started](#getting-started)
9 | - [Clone this repo](#clone-this-repo)
10 | - [Install dependences](#install-dependences)
11 | - [Build plugin](#build-plugin)
12 | - [Import plugin](#import-plugin)
13 | - [Run plugin](#run-plugin)
14 | - [Making a Pull Request](#making-a-pull-request)
15 |
16 | ## Requirements
17 |
18 | - [git](https://www.github.com/git-guides/install-git) - A version manager.
19 | - [Node (Gallium)](nodejs.org/download/release/latest-gallium) - The JavaScript runtime, versioned at ^16.
20 | - [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) - A package manager.
21 | - [Figma Desktop](https://www.figma.com/downloads/) - Required for running plug-ins in development.
22 | - Knowledge of the [Figma API](https://www.figma.com/plugin-docs) and [TypeScript](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html).
23 | - (Optional) Knowledge of [Tailwind](https://tailwindcss.com)
24 |
25 | ## Getting started
26 |
27 | ### Clone this repo
28 |
29 | ```bash
30 | git clone https://github.com/the-dataface/figma2html.git
31 | ```
32 |
33 | ### Install dependences
34 |
35 | The following command with move you to the repository's local directory, set up the correct Node version, and install the dependencies:
36 |
37 | ```bash
38 | cd figma2html
39 | nvm use
40 | npm install
41 | ```
42 |
43 | ### Build plugin
44 |
45 | After making updates locally, run `npm run build` in a terminal to bundle the project. There should be a `build` folder with three files: `bundle.js`, `index.html`, `main.js`. There are required to run the plugin.
46 |
47 | To watch for updates, run `npm run dev` in a terminal. This will start a server that will livereload the aforementioned files and let you develop while testing your Figma file.
48 |
49 | ### Import plugin
50 |
51 | 1. From Figma's top menu bar: `Plugins` > `Development` > `Import plugin from manifest`.
52 | 2. Select the `manifest.json` file in the cloned `figma2html` directory.
53 |
54 | ### Run plugin
55 |
56 | Via shortcut:
57 |
58 | 1. `cmd + /` to open the quick actions bar
59 | 2. Type `figma2html` and hit enter on the plugin. You will see a _development_ tag next to the version in development.
60 |
61 | Via menu:
62 |
63 | 1. From Figma's top menu: `Plugins` > `Development` > `figma2html`
64 |
65 | Via resources:
66 |
67 | 1. `shift + I` top open the resources panel
68 | 2. Click `Plugins`
69 | 3. Search for `figma2html` and click run
70 |
71 | ## Making a Pull Request
72 |
73 | - All pull requests should be made against the `main` branch.
74 | - Code should be in working condition before a review is requested unless help is needed. Ensure that the code is formatted with `npm run format` and that there are no linting errors with `npm run lint`.
75 | - Commits should follow the [conventional commits style](https://www.conventionalcommits.org/), which helps generate changelogs and ensure proper versioning.
76 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2023 [these people](https://github.com/the-dataface/figma2html/graphs/contributors)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # [figma2html](https://www.figma.com/community/plugin/1109185297790825980/figma2html)
4 |
5 | Export Figma frames to responsive HTML and CSS. A Figma version of [ai2html](http://ai2html.org), with additional features. Created by [The DataFace](https://www.thedataface.com).
6 |
7 | [Install the plugin](https://www.figma.com/community/plugin/1109185297790825980/figma2html) on Figma.
8 |
9 | ## Table of Contents
10 |
11 | - [figma2html](#figma2html)
12 | - [Table of Contents](#table-of-contents)
13 | - [Instructions](#instructions)
14 | - [How it Works](#how-it-works)
15 | - [Options + Features](#options--features)
16 | - [File settings](#file-settings)
17 | - [Image settings](#image-settings)
18 | - [Page settings](#page-settings)
19 | - [Text settings](#text-settings)
20 | - [Variable Text](#variable-text)
21 | - [Contributing + Feedback](#contributing--feedback)
22 | - [Acknowledgements](#acknowledgements)
23 | - [License](#license)
24 |
25 | ## Instructions
26 |
27 | 1. Create your artwork
28 | - Size frames according to the desired breakpoints. (note: breakpoints are based on width and not height).
29 | - Name frames using the following format: `#[width]px` (e.g. `#640px`).
30 | - If you're using a Google Font that's built in to Figma, you'll have the option to automatically include the appropriate Google Fonts tag in your exported HTML. Otherwise, you can add your webfonts to your project yourself or include a custom script or link any custom CSS in the `Page settings` panel.'
31 | 2. Run the plugin and configure your settings
32 | - In the plugin dialog, you can you load your preconfigured settings from a YAML text node named `f2h-settings` on the current page in your Figma doc, or you can write your current settings to a YAML text node.
33 | 3. Click Export and voila, you can save your exported images and HTML file as a zipped folder.
34 |
35 | Check out [this demo Figma file](https://www.figma.com/file/THVkWmLhe7TJD16hj0IDIR/figma2html-Demo?node-id=0%3A1) if you need a reference to get started!
36 |
37 | ## How it Works
38 |
39 | - The plugin grabs all frames named `#[width]px` on your current page, unframes all children (turns frames into groups to get positions relative to the parent frame), hides all text layers, and exports as a `PNG`, `JPG`, or `SVG`, at a specified scale.
40 | - All text elements are added via absolutely positioned divs.
41 | - Text is rendered as `
` (or header) elements. A style is applied based on the most frequently used style within that frame, with inline styles applied to text that differs. This should match what you see in Figma.
42 | - Text frames with auto widths are rendered with `width=auto`, while text frames with set widths include that width as inline css.
43 | - Text is rendered with any visible drop shadows and layer blurs as inline css.
44 | - The plugin supports rotated text, as well as left/right/center-aligned text.
45 | - If `Include Resizer Script` is turned on, the outputted HTML will include JS to show and hide the correct image at the appropriate size, based on the named width of the frame.
46 |
47 | ## Options + Features
48 |
49 | ### File settings
50 |
51 | - The inputted Filename will be the name of the outputted zipped folder and HTML file. You can customize this, but will default to the current page name in Figma. This field is required to export.
52 | - Filetype option so far is just `html`. `svelte` is coming soon!
53 | - Turn on `Testing Mode` to keep text visible on exported images. Useful for comparing layouts in the exported HTML vs the designs in Figma.
54 |
55 | ### Image settings
56 |
57 | - Input an `Image scale` to export images at a specified scale.
58 | - `Image format` options include `PNG`, `JPG`, and `SVG`.
59 | - The `Path` field allows you to specify an image path, which will be referenced in the image paths in the outputted HTML. Useful if you will be dropping your outputted files into a larger codebase.
60 | - `Alt text` allows you to add an alt text tag to images in your outputted HTML. This field is required to export.
61 |
62 | ### Page settings
63 |
64 | - Turn on `Include resizer script` to include JS in your outputted file which will show and hide the appropriate image at the appropriate size.
65 | - Turn on `Center HTML output` to center your page content in the outputted HTML.
66 | - Turn on `Fluid` to have the f2h containers fill the width of the screen. If off, f2h containers will maintain the widths at which they were designed in Figma.
67 | - Input a `Max width` in pixels to apply a max width to your page content.
68 | - Include `Custom scripts`, like Typekit tags, to your outputted HTML.
69 |
70 | ### Text settings
71 |
72 | - Turn on `Style text elements` to include inline styles to match the text styles you see in Figma.
73 | - Turn on `Include figma styles` as Classes to include any named styles in Figma as classes on your text elements. Ex. `Desktop/Utility Sans` will be included as `class="utility-sans"`.
74 | - Turn on `Include Google Fonts` to include a Google Fonts tag for included Google Fonts in the outputted HTML.
75 | - **Including custom attributes:** you can include custom attributes on a text layer by naming the layer `[f2h]` followed by a list of attributes in HTMl attribute format. For example, a text layer named `[f2h]class="text-sm font-sans" id="my-text-layer"` will render as `
`. This can be useful for including Tailwind and other utility classes. You also can use a reserved `tag` attribute to change the default `
` tag to one of your choosing (ex: `[f2h] tag="h1" id="hed" class="text-xl font-bold"` will render `
`). Note that naming your layers like this will not turn off `Style text elements` for that node, and will still include inline styling for child spans. To rely purely on class names for styling, turn off the `Style text elements` option.
76 |
77 | ### Variable Text
78 |
79 | Variable text is supported for exportable frames (frames prefixed with `#`). Variables can be added in the UI under the `Variables` panel. All variables values are strings and can be used in any text node in your Figma file.
80 |
81 | To use a variable, edit any desired _visible_ text node's name to be the key (e.g. `hed`) in double curly brackets (e.g. `{{hed}}`) in a _visible_ text node. The exported files with replace the key with the appropriate value (e.g. `{{hed}}` -> `figma2html`).
82 |
83 | This replacement occurs on the exported files and on Figma itself everytime a key or value changes.
84 |
85 | ## Contributing + Feedback
86 |
87 | We welcome feedback, bug reports and feature requests in the form of [Issues](https://github.com/the-dataface/figma2html/issues) or [PRs](https://www.github.com/the-dataface/figma2html/pulls). Alternatively, comments can be left on the Figma plugin community page.
88 |
89 | Further information on contributing can be found in [CONTRIBUTING.md](https://www.github.com/the-dataface/figma2html/blob/main/CONTRIBUTING.md).
90 |
91 | ## Acknowledgements
92 |
93 | Figma2html is not possible without [ai2html](https://www.ai2html.org), created by [Archie Tse](https://twitter.com/archietse) at _The New York Times_. Figma2html sprung from the same codebase, modified in time to add additional features specific to the Figma API. The plugin was inspired by [Thomas Lowry](https://www.github.com/thomas-lowry)'s [Figma Plugin DS Svelte](https://www.github.com/thomas-lowry/figma-plugin-ds-svelte) library.
94 |
95 | The core team from The DataFace features [Sam Vickars](https://www.twitter.com/samvickars), [Sawyer Click](https://www.sawyer.codes), and [Michael Hester](https://www.twitter.com/immichaelhester).
96 |
97 | ## License
98 |
99 | [MIT](https://www.github.com/the-dataface/figma2html/blob/main/LICENSE)
100 |
--------------------------------------------------------------------------------
/lefthook.yml:
--------------------------------------------------------------------------------
1 | # EXAMPLE USAGE:
2 | #
3 | # Refer for explanation to following link:
4 | # https://github.com/evilmartians/lefthook/blob/master/docs/configuration.md
5 |
6 | pre-commit:
7 | commands:
8 | prettier:
9 | glob: '*'
10 | run: npx prettier --ignore-unknown --write {staged_files} && git add {staged_files}
11 |
12 | pre-push:
13 | commands:
14 | lint:
15 | glob: '*'
16 | run: npx eslint --fix {push_files} --ignore-pattern "**/*.md" --ignore-pattern "**/*.yml" && git add {push_files}
17 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "figma2html",
3 | "api": "1.0.0",
4 | "id": "1109185297790825980",
5 | "main": "build/main.js",
6 | "ui": "build/index.html",
7 | "editorType": ["figma"]
8 | }
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "figma2html",
3 | "version": "1.1.0",
4 | "private": true,
5 | "description": "Export Figma frames as images with text elements rendered as HTML",
6 | "homepage": "https://www.github.com/the-dataface/figma2html",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://www.github.com/the-dataface/figma2html.git"
10 | },
11 | "bugs": {
12 | "email": "sam@thedataface.com",
13 | "url": "https://www.github.com/the-dataface/figma2html/issues/new/choose"
14 | },
15 | "license": "MIT",
16 | "keywords": [
17 | "ai2html",
18 | "figma",
19 | "figma2html",
20 | "figma html"
21 | ],
22 | "scripts": {
23 | "dev": "rollup -c -w",
24 | "build": "rollup -c && rm ./build/bundle.js",
25 | "ci:version": "changeset version",
26 | "ci:publish": "changeset publish && git push --follow-tags"
27 | },
28 | "author": {
29 | "name": "Sam Vickars",
30 | "email": "sam.vickars@gmail.com",
31 | "url": "https://www.samvickars.com"
32 | },
33 | "contributors": [
34 | {
35 | "name": "Sawyer Click",
36 | "email": "sawyerclick@gmail.com",
37 | "url": "https://www.sawyer.codes"
38 | },
39 | {
40 | "name": "Michael Hester"
41 | }
42 | ],
43 | "devDependencies": {
44 | "@changesets/cli": "^2.26.0",
45 | "@figma/plugin-typings": "^1.77.0",
46 | "@rollup/plugin-alias": "^4.0.2",
47 | "@rollup/plugin-babel": "^6.0.3",
48 | "@rollup/plugin-commonjs": "24.0.0",
49 | "@rollup/plugin-dsv": "^3.0.1",
50 | "@rollup/plugin-html": "^1.0.1",
51 | "@rollup/plugin-image": "3.0.1",
52 | "@rollup/plugin-json": "^6.0.1",
53 | "@rollup/plugin-node-resolve": "^15.0.1",
54 | "@rollup/plugin-terser": "^0.2.1",
55 | "@rollup/plugin-typescript": "10.0.1",
56 | "@svitejs/changesets-changelog-github-compact": "^1.1.0",
57 | "@typescript-eslint/eslint-plugin": "^5.48.2",
58 | "@typescript-eslint/parser": "^5.48.2",
59 | "autoprefixer": "^10.4.13",
60 | "caniuse-lite": "1.0.30001441",
61 | "cssnano": "^5.1.14",
62 | "eslint": "^8.30.0",
63 | "eslint-config-prettier": "^8.5.0",
64 | "eslint-plugin-svelte3": "^4.0.0",
65 | "htmlparser2": "^9.0.0",
66 | "lefthook": "^1.2.7",
67 | "postcss": "^8.4.20",
68 | "postcss-load-config": "^4.0.1",
69 | "prettier": "^2.8.3",
70 | "prettier-plugin-svelte": "^2.9.0",
71 | "prettier-plugin-tailwindcss": "^0.2.1",
72 | "rollup": "3.8.1",
73 | "rollup-plugin-livereload": "2.0.5",
74 | "rollup-plugin-postcss": "^4.0.2",
75 | "rollup-plugin-svelte": "7.1.0",
76 | "rollup-plugin-svg": "2.0.0",
77 | "sirv-cli": "2.0.2",
78 | "slugify": "^1.6.5",
79 | "svelte": "3.55.0",
80 | "svelte-feather-icons": "^4.0.0",
81 | "svelte-preprocess": "5.0.0",
82 | "tailwindcss": "^3.2.4",
83 | "tslib": "2.4.1",
84 | "typescript": "4.9.4"
85 | },
86 | "dependencies": {
87 | "js-convert-case": "^4.2.0",
88 | "jszip": "3.10.1"
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {}
5 | }
6 | };
7 |
--------------------------------------------------------------------------------
/rollup.config.mjs:
--------------------------------------------------------------------------------
1 | import babel from '@rollup/plugin-babel';
2 | import commonjs from '@rollup/plugin-commonjs';
3 | import dsv from '@rollup/plugin-dsv';
4 | import htmlBundle, { makeHtmlAttributes } from '@rollup/plugin-html';
5 | import json from '@rollup/plugin-json';
6 | import resolve from '@rollup/plugin-node-resolve';
7 | import terser from '@rollup/plugin-terser';
8 | import typescript from '@rollup/plugin-typescript';
9 | import cssnano from 'cssnano';
10 | import { readFile } from 'node:fs/promises';
11 | import livereload from 'rollup-plugin-livereload';
12 | import postcss from 'rollup-plugin-postcss';
13 | import svelte from 'rollup-plugin-svelte';
14 | import svg from 'rollup-plugin-svg';
15 | import sveltePreprocess from 'svelte-preprocess';
16 |
17 | const fileUrl = new URL('./package.json', import.meta.url);
18 | const pkg = JSON.parse(await readFile(fileUrl, 'utf8'));
19 |
20 | const production = !process.env.ROLLUP_WATCH;
21 |
22 | export default [
23 | {
24 | input: 'src/app.js',
25 | output: {
26 | format: 'iife',
27 | name: 'ui',
28 | file: 'build/bundle.js'
29 | },
30 | plugins: [
31 | svelte({
32 | preprocess: sveltePreprocess({
33 | sourceMap: !production,
34 | postcss: true
35 | }),
36 | compilerOptions: {
37 | dev: !production
38 | }
39 | }),
40 | resolve({
41 | browser: true,
42 | dedupe: (importee) => importee === 'svelte' || importee.startsWith('svelte/'),
43 | extensions: ['.svelte', '.mjs', '.js', '.json', '.node']
44 | }),
45 | typescript(),
46 | commonjs(),
47 | babel({ babelHelpers: 'bundled' }),
48 | svg(),
49 | dsv(),
50 | json(),
51 | postcss({
52 | extensions: ['.css'],
53 | plugins: [cssnano()]
54 | }),
55 | htmlBundle({
56 | fileName: 'index.html',
57 | target: 'build/index.html',
58 | title: pkg.name,
59 | attributes: {
60 | html: { lang: 'en' },
61 | link: null,
62 | script: null
63 | },
64 | meta: [
65 | { charset: 'utf-8' },
66 | { name: 'viewport', content: 'width=device-width, initial-scale=1' },
67 | { name: 'description', content: `${pkg.description} - v${pkg.version}` }
68 | ],
69 | template: ({ attributes, files, publicPath, title, meta }) => {
70 | // inline all script to fit figma's one-file rule
71 | const scripts = (files.js || []).map(({ code }) => ``).join('\n');
72 |
73 | const links = (files.css || [])
74 | .map(
75 | ({ fileName }) =>
76 | ``
79 | )
80 | .join('\n');
81 |
82 | const metas = meta.map((input) => ``).join('\n');
83 |
84 | const html = makeHtmlAttributes(attributes.html);
85 |
86 | return `${title}${metas}${links}${scripts}`;
87 | }
88 | }),
89 | !production && livereload('build'),
90 | production && terser()
91 | ],
92 | watch: {
93 | clearScreen: false
94 | }
95 | },
96 | {
97 | input: 'src/main.ts',
98 | output: {
99 | file: 'build/main.js',
100 | format: 'cjs',
101 | name: 'main',
102 | inlineDynamicImports: false
103 | },
104 | plugins: [
105 | typescript(),
106 | resolve(),
107 | commonjs(),
108 | json(),
109 | babel({ babelHelpers: 'bundled' }),
110 | production && terser()
111 | ]
112 | }
113 | ];
114 |
--------------------------------------------------------------------------------
/src/App.svelte:
--------------------------------------------------------------------------------
1 |
261 |
262 |
24 |
25 |
28 |
29 | How do I use Variables?
30 |
31 |
32 |
33 |
34 | Figma2html's Variables are a way to simultaneously set the text content of several nodes.
35 |
36 |
37 | Variables can occur in two modes: whole or partial. Whole variables will replace the
38 | entire text content of a node and are previewable, while partial variables will replace
39 | the variable in a text node only on export.
40 |
41 |
42 | Get started with whole variables by renaming a text node in the left-hand panel to a
43 | variable name of your choosing, excluding spaces. Wrap the variable name in two curly
44 | braces on each side: {`{{variable}}`}.
45 |
46 |
47 | Then, open figma2html and add that same name in the Variables panel. Add an associated
48 | value in the text area, and hit the button to preview the text.
52 |
53 |
54 | Valid HTML is supported on whole and partial variables on export, but only pure text
55 | content is previewable in Figma.
56 |
57 |
58 |
59 |
60 |
61 | {#if entries.length}
62 |
63 | {#each entries as [uuid, { key, value }] (uuid)}
64 | {@const id = `variable-${slugify(uuid, { lower: true, strict: true })}`}
65 |