├── .github
└── CODEOWNERS
├── .gitignore
├── .vercelignore
├── .yarnrc
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── api
├── _fonts
│ ├── Inter-Bold.woff2
│ ├── Inter-License.txt
│ ├── Inter-Regular.woff2
│ ├── Vera-License.txt
│ └── Vera-Mono.woff2
├── _lib
│ ├── chromium.ts
│ ├── options.ts
│ ├── parser.ts
│ ├── sanitizer.ts
│ ├── template.ts
│ └── types.ts
├── heropatterns.d.ts
├── index.ts
└── tsconfig.json
├── package.json
├── public
├── favicon.ico
├── index.html
├── robots.txt
├── style.css
└── tweet.png
├── vercel.json
├── web
├── index.ts
└── tsconfig.json
└── yarn.lock
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @styfle
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .now
2 | .vercel
3 | node_modules
4 | api/dist
5 | public/dist
6 |
7 |
--------------------------------------------------------------------------------
/.vercelignore:
--------------------------------------------------------------------------------
1 | .github
2 | node_modules
3 | api/dist
4 | public/dist
5 | CONTRIBUTING.md
6 | README.md
7 |
--------------------------------------------------------------------------------
/.yarnrc:
--------------------------------------------------------------------------------
1 | save-prefix ""
2 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | There are two pieces to `og-image` that are worth noting before you begin development.
4 |
5 | 1. The backend image generator located in [/api/index.ts](https://github.com/vercel/og-image/blob/main/api/index.ts)
6 | 2. The frontend inputs located in [/web/index.ts](https://github.com/vercel/og-image/blob/main/web/index.ts)
7 |
8 | Vercel handles [routing](https://github.com/vercel/og-image/blob/main/vercel.json#L6) in an elegant way for us so deployment is easy.
9 |
10 | To start hacking, do the following:
11 |
12 | 1. Clone this repo with `git clone https://github.com/vercel/og-image`
13 | 2. Change directory with `cd og-image`
14 | 3. Run `yarn` or `npm install` to install all dependencies
15 | 4. Run locally with `vercel dev` and visit [localhost:3000](http://localhost:3000) (if nothing happens, run `npm install -g vercel`)
16 | 5. If necessary, edit the `exePath` in [options.ts](https://github.com/vercel/og-image/blob/main/api/_lib/options.ts) to point to your local Chrome executable
17 |
18 | Now you're ready to start local development!
19 |
20 | You can set an environment variable to assist with debugging `export OG_HTML_DEBUG=1`. This will render the image as HTML so you can play around with your browser's dev tools before committing changes to the template.
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019-2020 Vercel, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # [Social Images for PHP Packages](https://banners.beyondco.de)
4 |
5 | 
6 |
7 | Serverless service that generates dynamic Open Graph images that you can embed in your `` tags.
8 |
9 | For each keystroke, headless chromium is used to render an HTML page and take a screenshot of the result which gets cached.
10 |
11 | See the image embedded in the tweet for a real use case.
12 |
13 |
14 | ## What is an Open Graph Image?
15 |
16 | Have you ever posted a hyperlink to Twitter, Facebook, or Slack and seen an image popup?
17 | How did your social network know how to "unfurl" the URL and get an image?
18 | The answer is in your `
`.
19 |
20 | The [Open Graph protocol](http://ogp.me) says you can put a `` tag in the `` of a webpage to define this image.
21 |
22 | It looks like the following:
23 |
24 | ```html
25 |
26 | Title
27 |
28 |
29 | ```
30 |
31 | ## Why use this service?
32 |
33 | This service lets you generate beautiful looking social images for your PHP, Javascript, Python and Laravel packages. You have already put a lot of time and effort into your package, so why not make it look beautiful using this service.
34 |
35 | ## Authors
36 |
37 | - Steven ([@styfle](https://twitter.com/styfle)) - [Vercel](https://vercel.com)
38 | - Evil Rabbit ([@evilrabbit_](https://twitter.com/evilrabbit_)) - [Vercel](https://vercel.com)
39 | - Marcel Pociot ([@marcelpociot](https://twitter.com/marcelpociot)) - [Beyond Code](https://beyondco.de)
40 |
--------------------------------------------------------------------------------
/api/_fonts/Inter-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beyondcode/banners/a365a00331282991f01d811c490cf6270ee7e2ef/api/_fonts/Inter-Bold.woff2
--------------------------------------------------------------------------------
/api/_fonts/Inter-License.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016-2018 The Inter Project Authors (me@rsms.me)
2 |
3 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
4 | This license is copied below, and is also available with a FAQ at:
5 | http://scripts.sil.org/OFL
6 |
7 | -----------------------------------------------------------
8 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
9 | -----------------------------------------------------------
10 |
11 | PREAMBLE
12 | The goals of the Open Font License (OFL) are to stimulate worldwide
13 | development of collaborative font projects, to support the font creation
14 | efforts of academic and linguistic communities, and to provide a free and
15 | open framework in which fonts may be shared and improved in partnership
16 | with others.
17 |
18 | The OFL allows the licensed fonts to be used, studied, modified and
19 | redistributed freely as long as they are not sold by themselves. The
20 | fonts, including any derivative works, can be bundled, embedded,
21 | redistributed and/or sold with any software provided that any reserved
22 | names are not used by derivative works. The fonts and derivatives,
23 | however, cannot be released under any other type of license. The
24 | requirement for fonts to remain under this license does not apply
25 | to any document created using the fonts or their derivatives.
26 |
27 | DEFINITIONS
28 | "Font Software" refers to the set of files released by the Copyright
29 | Holder(s) under this license and clearly marked as such. This may
30 | include source files, build scripts and documentation.
31 |
32 | "Reserved Font Name" refers to any names specified as such after the
33 | copyright statement(s).
34 |
35 | "Original Version" refers to the collection of Font Software components as
36 | distributed by the Copyright Holder(s).
37 |
38 | "Modified Version" refers to any derivative made by adding to, deleting,
39 | or substituting -- in part or in whole -- any of the components of the
40 | Original Version, by changing formats or by porting the Font Software to a
41 | new environment.
42 |
43 | "Author" refers to any designer, engineer, programmer, technical
44 | writer or other person who contributed to the Font Software.
45 |
46 | PERMISSION AND CONDITIONS
47 | Permission is hereby granted, free of charge, to any person obtaining
48 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
49 | redistribute, and sell modified and unmodified copies of the Font
50 | Software, subject to the following conditions:
51 |
52 | 1) Neither the Font Software nor any of its individual components,
53 | in Original or Modified Versions, may be sold by itself.
54 |
55 | 2) Original or Modified Versions of the Font Software may be bundled,
56 | redistributed and/or sold with any software, provided that each copy
57 | contains the above copyright notice and this license. These can be
58 | included either as stand-alone text files, human-readable headers or
59 | in the appropriate machine-readable metadata fields within text or
60 | binary files as long as those fields can be easily viewed by the user.
61 |
62 | 3) No Modified Version of the Font Software may use the Reserved Font
63 | Name(s) unless explicit written permission is granted by the corresponding
64 | Copyright Holder. This restriction only applies to the primary font name as
65 | presented to the users.
66 |
67 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
68 | Software shall not be used to promote, endorse or advertise any
69 | Modified Version, except to acknowledge the contribution(s) of the
70 | Copyright Holder(s) and the Author(s) or with their explicit written
71 | permission.
72 |
73 | 5) The Font Software, modified or unmodified, in part or in whole,
74 | must be distributed entirely under this license, and must not be
75 | distributed under any other license. The requirement for fonts to
76 | remain under this license does not apply to any document created
77 | using the Font Software.
78 |
79 | TERMINATION
80 | This license becomes null and void if any of the above conditions are
81 | not met.
82 |
83 | DISCLAIMER
84 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
85 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
86 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
87 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
88 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
89 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
90 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
91 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
92 | OTHER DEALINGS IN THE FONT SOFTWARE.
93 |
--------------------------------------------------------------------------------
/api/_fonts/Inter-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beyondcode/banners/a365a00331282991f01d811c490cf6270ee7e2ef/api/_fonts/Inter-Regular.woff2
--------------------------------------------------------------------------------
/api/_fonts/Vera-License.txt:
--------------------------------------------------------------------------------
1 | Bitstream Vera Fonts Copyright
2 |
3 | The fonts have a generous copyright, allowing derivative works (as
4 | long as "Bitstream" or "Vera" are not in the names), and full
5 | redistribution (so long as they are not *sold* by themselves). They
6 | can be be bundled, redistributed and sold with any software.
7 |
8 | The fonts are distributed under the following copyright:
9 |
10 | Copyright
11 | =========
12 |
13 | Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream
14 | Vera is a trademark of Bitstream, Inc.
15 |
16 | Permission is hereby granted, free of charge, to any person obtaining
17 | a copy of the fonts accompanying this license ("Fonts") and associated
18 | documentation files (the "Font Software"), to reproduce and distribute
19 | the Font Software, including without limitation the rights to use,
20 | copy, merge, publish, distribute, and/or sell copies of the Font
21 | Software, and to permit persons to whom the Font Software is furnished
22 | to do so, subject to the following conditions:
23 |
24 | The above copyright and trademark notices and this permission notice
25 | shall be included in all copies of one or more of the Font Software
26 | typefaces.
27 |
28 | The Font Software may be modified, altered, or added to, and in
29 | particular the designs of glyphs or characters in the Fonts may be
30 | modified and additional glyphs or characters may be added to the
31 | Fonts, only if the fonts are renamed to names not containing either
32 | the words "Bitstream" or the word "Vera".
33 |
34 | This License becomes null and void to the extent applicable to Fonts
35 | or Font Software that has been modified and is distributed under the
36 | "Bitstream Vera" names.
37 |
38 | The Font Software may be sold as part of a larger software package but
39 | no copy of one or more of the Font Software typefaces may be sold by
40 | itself.
41 |
42 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
43 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
44 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
45 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
46 | BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR
47 | OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL,
48 | OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
49 | OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT
50 | SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
51 |
52 | Except as contained in this notice, the names of Gnome, the Gnome
53 | Foundation, and Bitstream Inc., shall not be used in advertising or
54 | otherwise to promote the sale, use or other dealings in this Font
55 | Software without prior written authorization from the Gnome Foundation
56 | or Bitstream Inc., respectively. For further information, contact:
57 | fonts at gnome dot org.
58 |
59 | Copyright FAQ
60 | =============
61 |
62 | 1. I don't understand the resale restriction... What gives?
63 |
64 | Bitstream is giving away these fonts, but wishes to ensure its
65 | competitors can't just drop the fonts as is into a font sale system
66 | and sell them as is. It seems fair that if Bitstream can't make money
67 | from the Bitstream Vera fonts, their competitors should not be able to
68 | do so either. You can sell the fonts as part of any software package,
69 | however.
70 |
71 | 2. I want to package these fonts separately for distribution and
72 | sale as part of a larger software package or system. Can I do so?
73 |
74 | Yes. A RPM or Debian package is a "larger software package" to begin
75 | with, and you aren't selling them independently by themselves.
76 | See 1. above.
77 |
78 | 3. Are derivative works allowed?
79 | Yes!
80 |
81 | 4. Can I change or add to the font(s)?
82 | Yes, but you must change the name(s) of the font(s).
83 |
84 | 5. Under what terms are derivative works allowed?
85 |
86 | You must change the name(s) of the fonts. This is to ensure the
87 | quality of the fonts, both to protect Bitstream and Gnome. We want to
88 | ensure that if an application has opened a font specifically of these
89 | names, it gets what it expects (though of course, using fontconfig,
90 | substitutions could still could have occurred during font
91 | opening). You must include the Bitstream copyright. Additional
92 | copyrights can be added, as per copyright law. Happy Font Hacking!
93 |
94 | 6. If I have improvements for Bitstream Vera, is it possible they might get
95 | adopted in future versions?
96 |
97 | Yes. The contract between the Gnome Foundation and Bitstream has
98 | provisions for working with Bitstream to ensure quality additions to
99 | the Bitstream Vera font family. Please contact us if you have such
100 | additions. Note, that in general, we will want such additions for the
101 | entire family, not just a single font, and that you'll have to keep
102 | both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add
103 | glyphs to the font, they must be stylistically in keeping with Vera's
104 | design. Vera cannot become a "ransom note" font. Jim Lyles will be
105 | providing a document describing the design elements used in Vera, as a
106 | guide and aid for people interested in contributing to Vera.
107 |
108 | 7. I want to sell a software package that uses these fonts: Can I do so?
109 |
110 | Sure. Bundle the fonts with your software and sell your software
111 | with the fonts. That is the intent of the copyright.
112 |
113 | 8. If applications have built the names "Bitstream Vera" into them,
114 | can I override this somehow to use fonts of my choosing?
115 |
116 | This depends on exact details of the software. Most open source
117 | systems and software (e.g., Gnome, KDE, etc.) are now converting to
118 | use fontconfig (see www.fontconfig.org) to handle font configuration,
119 | selection and substitution; it has provisions for overriding font
120 | names and subsituting alternatives. An example is provided by the
121 | supplied local.conf file, which chooses the family Bitstream Vera for
122 | "sans", "serif" and "monospace". Other software (e.g., the XFree86
123 | core server) has other mechanisms for font substitution.
124 |
125 |
--------------------------------------------------------------------------------
/api/_fonts/Vera-Mono.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beyondcode/banners/a365a00331282991f01d811c490cf6270ee7e2ef/api/_fonts/Vera-Mono.woff2
--------------------------------------------------------------------------------
/api/_lib/chromium.ts:
--------------------------------------------------------------------------------
1 | import { launch, Page } from 'puppeteer-core';
2 | import { getOptions } from './options';
3 | import { FileType } from './types';
4 | let _page: Page | null;
5 |
6 | async function getPage(isDev: boolean) {
7 | if (_page) {
8 | return _page;
9 | }
10 | const options = await getOptions(isDev);
11 | const browser = await launch(options);
12 | _page = await browser.newPage();
13 | return _page;
14 | }
15 |
16 | export async function getScreenshot(html: string, type: FileType, isDev: boolean) {
17 | const page = await getPage(isDev);
18 | await page.setViewport({ width: 2048, height: 1170 });
19 | await page.setContent(html);
20 | const file = await page.screenshot({ type });
21 | return file;
22 | }
23 |
--------------------------------------------------------------------------------
/api/_lib/options.ts:
--------------------------------------------------------------------------------
1 | import chrome from 'chrome-aws-lambda';
2 | const exePath = process.platform === 'win32'
3 | ? 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe'
4 | : process.platform === 'linux'
5 | ? '/usr/bin/google-chrome'
6 | : '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
7 |
8 | interface Options {
9 | args: string[];
10 | executablePath: string;
11 | headless: boolean;
12 | }
13 |
14 | export async function getOptions(isDev: boolean) {
15 | let options: Options;
16 | if (isDev) {
17 | options = {
18 | args: [],
19 | executablePath: exePath,
20 | headless: true
21 | };
22 | } else {
23 | options = {
24 | args: chrome.args,
25 | executablePath: await chrome.executablePath,
26 | headless: chrome.headless,
27 | };
28 | }
29 | return options;
30 | }
31 |
--------------------------------------------------------------------------------
/api/_lib/parser.ts:
--------------------------------------------------------------------------------
1 | import { IncomingMessage } from 'http';
2 | import { parse } from 'url';
3 | import { ParsedRequest } from './types';
4 |
5 | export function parseRequest(req: IncomingMessage) {
6 | console.log('HTTP ' + req.url);
7 | const { pathname, query } = parse(req.url || '/', true);
8 | const { fontSize, images, widths, heights, theme, md, showWatermark, pattern, packageManager, packageName, description, style } = (query || {});
9 |
10 | if (Array.isArray(fontSize)) {
11 | throw new Error('Expected a single fontSize');
12 | }
13 | if (Array.isArray(theme)) {
14 | throw new Error('Expected a single theme');
15 | }
16 | if (Array.isArray(style)) {
17 | throw new Error('Expected a single style');
18 | }
19 | if (Array.isArray(pattern)) {
20 | throw new Error('Expected a single pattern');
21 | }
22 | if (Array.isArray(packageManager)) {
23 | throw new Error('Expected a single package manager');
24 | }
25 | if (Array.isArray(packageName)) {
26 | throw new Error('Expected a single package name');
27 | }
28 | if (Array.isArray(description)) {
29 | throw new Error('Expected a single package name');
30 | }
31 |
32 | const arr = (pathname || '/').slice(1).split('.');
33 | let extension = '';
34 | let text = '';
35 | if (arr.length === 0) {
36 | text = '';
37 | } else if (arr.length === 1) {
38 | text = arr[0];
39 | } else {
40 | extension = arr.pop() as string;
41 | text = arr.join('.');
42 | }
43 |
44 | const parsedRequest: ParsedRequest = {
45 | fileType: extension === 'jpeg' ? extension : 'png',
46 | text: decodeURIComponent(text),
47 | theme: theme === 'dark' ? 'dark' : 'light',
48 | style: style === 'style_2' ? 'style_2' : 'style_1',
49 | md: md === '1' || md === 'true',
50 | showWatermark: showWatermark === '1' || showWatermark === 'true',
51 | fontSize: fontSize || '96px',
52 | pattern: pattern || 'circuitBoard',
53 | packageManager: packageManager || '',
54 | packageName: packageName || '',
55 | description: description || '',
56 | images: getArray(images),
57 | widths: getArray(widths),
58 | heights: getArray(heights),
59 | };
60 | parsedRequest.images = getDefaultImages(parsedRequest.images);
61 | return parsedRequest;
62 | }
63 |
64 | function getArray(stringOrArray: string[] | string | undefined): string[] {
65 | if (typeof stringOrArray === 'undefined') {
66 | return [];
67 | } else if (Array.isArray(stringOrArray)) {
68 | return stringOrArray;
69 | } else {
70 | return [stringOrArray];
71 | }
72 | }
73 |
74 | function getDefaultImages(images: string[]): string[] {
75 | const defaultImage = 'https://laravel.com/img/logomark.min.svg';
76 |
77 | if (!images || !images[0]) {
78 | return [defaultImage];
79 | }
80 | return images;
81 | }
82 |
--------------------------------------------------------------------------------
/api/_lib/sanitizer.ts:
--------------------------------------------------------------------------------
1 | const entityMap: { [key: string]: string } = {
2 | "&": "&",
3 | "<": "<",
4 | ">": ">",
5 | '"': '"',
6 | "'": ''',
7 | "/": '/'
8 | };
9 |
10 | export function sanitizeHtml(html: string) {
11 | return String(html).replace(/[&<>"'\/]/g, key => entityMap[key]);
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/api/_lib/template.ts:
--------------------------------------------------------------------------------
1 |
2 | import marked from 'marked';
3 | import { sanitizeHtml } from './sanitizer';
4 | import { ParsedRequest, Style, Theme } from './types';
5 | import * as hero from 'hero-patterns';
6 | import fs from 'fs';
7 |
8 | const twemoji = require('twemoji');
9 | const twOptions = { folder: 'svg', ext: '.svg' };
10 | const emojify = (text: string) => twemoji.parse(text, twOptions);
11 |
12 | function getCss(theme: string, pattern: string, fontSize: string) {
13 | let foreground = '#000000';
14 | let background = '#ffffff';
15 | let opacity = 0.07;
16 |
17 | if (theme === 'dark') {
18 | foreground = '#ffffff';
19 | background = '#000000';
20 | opacity = 0.15;
21 | }
22 | return `
23 | body {
24 | font-family: Inter;
25 | background-color: ${background};
26 | background-image: ${hero[pattern](foreground, opacity)}
27 | }
28 |
29 | code {
30 | color: #ff2d20;
31 | font-size: 2vw;
32 | font-family: 'Space Mono';
33 | font-weight: bold;
34 | white-space: pre-wrap;
35 | }
36 |
37 | code:before, code:after {
38 | content: '\`';
39 | }
40 |
41 | .logo-wrapper {
42 | display: flex;
43 | align-items: center;
44 | align-content: center;
45 | justify-content: center;
46 | justify-items: center;
47 | }
48 |
49 | .logo {
50 | margin: 0 75px;
51 | }
52 |
53 | .spacer {
54 | margin: 150px;
55 | }
56 |
57 | .emoji {
58 | height: 1em;
59 | width: 1em;
60 | margin: 0 .05em 0 .1em;
61 | vertical-align: -0.1em;
62 | }
63 |
64 | .heading {
65 | font-size: ${sanitizeHtml(fontSize)};
66 | font-style: normal;
67 | color: ${foreground};
68 | font-family: 'Inter', sans-serif;
69 | font-weight: 800;
70 | line-height: 1.2;
71 | }
72 |
73 | .description {
74 | color: ${foreground};
75 | }
76 |
77 | .text-laravel {
78 | color: #ff2d20;
79 | }
80 | `;
81 | }
82 |
83 | function getDescription(description: string) {
84 | if (description === '' || description === undefined) {
85 | return '';
86 | }
87 |
88 | return `
89 |
76 | Open Source Packages
77 | Open Source Packages
78 |
79 |
80 |
81 | You put all your work into creating an awesome Open Source package - now it's time to make it shine! Use this generator to create beautiful looking social images for your package.
82 |