Repository: anuraghazra/github-readme-stats
Pull request #2529 (2023-02-19 05:08:27)

Added "Shadow" set (Red, Green, Blue, transparent BG)
3 additional themes sticking primarily to flat colors, which the exception of icons and border being slightly darker. All 3 themes also have transparent backgrounds that will show differently per-user via GiHub's own light and dark themes. Transparency should also still provide easy readability for both. Previews are below for both of GitHub's light and dark modes.

![GitHub_Stats_Shadow_Dark](https://user-images.githubusercontent.com/55290878/219923365-6140815b-742c-4b99-bf84-4daa8b5fdc54.PNG)
![GitHub_Stats_Shadow_Light](https://user-images.githubusercontent.com/55290878/219923367-44b8ec47-fbfd-4cf9-a697-2c73a2051b33.PNG)


vercel[bot]: @Etanarvazac is attempting to deploy a commit to the **github readme stats** Team on [Vercel](https://vercel.com).

A member of the Team first needs to [authorize it](https://vercel.com/git/authorize?team=github%20readme%20stats&type=github&job=%7B%22headInfo%22%3A%7B%22sha%22%3A%229b654bd210284782e74e5afb997907b740eacb78%22%7D%2C%22id%22%3A%22QmRLc763zK5hjew6tQzs7gzxqosxruPLshpf1jE9bsgZsz%22%2C%22org%22%3A%22anuraghazra%22%2C%22prId%22%3A2529%2C%22repo%22%3A%22github-readme-stats%22%7D).


github-actions[bot]: 
      # Automated Theme Preview
      
  Hi, thanks for the theme contribution. Please read our theme [contribution guidelines](https://github.com/anuraghazra/github-readme-stats/blob/master/CONTRIBUTING.md#themes-contribution).
  We are currently only accepting color combinations from any VSCode theme or themes with good colour combinations to minimize bloating the themes collection.

  > Also, note that if this theme is exclusively for your personal use, then instead of adding it to our theme collection, you can use card [customization options](https://github.com/anuraghazra/github-readme-stats#customization).

    
      :x: Theme PR does not adhere to our guidelines.
      ## Test results
      - :x: shadow_red- :x: shadow_green- :x: shadow_blue

      **Result:** :x: Some themes are invalid.


  Unfortunately, your theme PR contains an error or does not adhere to our [theme guidelines](https://github.com/anuraghazra/github-readme-stats/blob/master/CONTRIBUTING.md#themes-contribution). Please fix the issues below, and we will review your   PR again. This pull request will **automatically close in 20 days** if no changes are made. After this time, you must re-open the PR for it to be reviewed.

      
      ## Details
      
        ### Shadow_red theme preview
        
        - :warning: `title_color` does not pass [AA contrast ratio](https://webaim.org/resources/contrastchecker/?fcolor=9A0000&bcolor=111).
- :warning: `icon_color` does not pass [AA contrast ratio](https://webaim.org/resources/contrastchecker/?fcolor=4F0000&bcolor=111).
- :warning: `text_color` does not pass [AA contrast ratio](https://webaim.org/resources/contrastchecker/?fcolor=444&bcolor=111).


        
title_color: <code>#9A0000</code> | icon_color: <code>#4F0000</code> | text_color: <code>#444</code> | bg_color: <code>#1116</code> | border_color: <code>#4F0000</code>

        [Preview Link](https://github-readme-stats.vercel.app/api?username=anuraghazra&title_color=9A0000&text_color=444&icon_color=4F0000&border_color=4F0000&bg_color=1116&show_icons=true)

        [![](https://github-readme-stats.vercel.app/api?username=anuraghazra&title_color=9A0000&text_color=444&icon_color=4F0000&border_color=4F0000&bg_color=1116&show_icons=true)](https://github-readme-stats.vercel.app/api?username=anuraghazra&title_color=9A0000&text_color=444&icon_color=4F0000&border_color=4F0000&bg_color=1116&show_icons=true)
      
        ### Shadow_green theme preview
        
        - :warning: `title_color` does not pass [AA contrast ratio](https://webaim.org/resources/contrastchecker/?fcolor=007A00&bcolor=111).
- :warning: `icon_color` does not pass [AA contrast ratio](https://webaim.org/resources/contrastchecker/?fcolor=003D00&bcolor=111).
- :warning: `text_color` does not pass [AA contrast ratio](https://webaim.org/resources/contrastchecker/?fcolor=444&bcolor=111).


        
title_color: <code>#007A00</code> | icon_color: <code>#003D00</code> | text_color: <code>#444</code> | bg_color: <code>#1116</code> | border_color: <code>#003D00</code>

        [Preview Link](https://github-readme-stats.vercel.app/api?username=anuraghazra&title_color=007A00&text_color=444&icon_color=003D00&border_color=003D00&bg_color=1116&show_icons=true)

        [![](https://github-readme-stats.vercel.app/api?username=anuraghazra&title_color=007A00&text_color=444&icon_color=003D00&border_color=003D00&bg_color=1116&show_icons=true)](https://github-readme-stats.vercel.app/api?username=anuraghazra&title_color=007A00&text_color=444&icon_color=003D00&border_color=003D00&bg_color=1116&show_icons=true)
      
        ### Shadow_blue theme preview
        
        - :warning: `title_color` does not pass [AA contrast ratio](https://webaim.org/resources/contrastchecker/?fcolor=00779A&bcolor=111).
- :warning: `icon_color` does not pass [AA contrast ratio](https://webaim.org/resources/contrastchecker/?fcolor=004450&bcolor=111).
- :warning: `text_color` does not pass [AA contrast ratio](https://webaim.org/resources/contrastchecker/?fcolor=444&bcolor=111).


        
title_color: <code>#00779A</code> | icon_color: <code>#004450</code> | text_color: <code>#444</code> | bg_color: <code>#1116</code> | border_color: <code>#004490</code>

        [Preview Link](https://github-readme-stats.vercel.app/api?username=anuraghazra&title_color=00779A&text_color=444&icon_color=004450&border_color=004490&bg_color=1116&show_icons=true)

        [![](https://github-readme-stats.vercel.app/api?username=anuraghazra&title_color=00779A&text_color=444&icon_color=004450&border_color=004490&bg_color=1116&show_icons=true)](https://github-readme-stats.vercel.app/api?username=anuraghazra&title_color=00779A&text_color=444&icon_color=004450&border_color=004490&bg_color=1116&show_icons=true)
      
    
Etanarvazac: It's meant to be darker than average. If everything was up to WebAIM, even dark mode would be too bright...
codecov[bot]: # [Codecov](https://codecov.io/gh/anuraghazra/github-readme-stats/pull/2529?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Anurag+Hazra) Report
Base: **97.27**% // Head: **97.28**% // Increases project coverage by **`+0.01%`** :tada:
> Coverage data is based on head [(`376eb45`)](https://codecov.io/gh/anuraghazra/github-readme-stats/pull/2529?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Anurag+Hazra) compared to base [(`ba7c2f8`)](https://codecov.io/gh/anuraghazra/github-readme-stats/commit/ba7c2f8b55eac8452e479c8bd38b044d204d0424?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Anurag+Hazra).
> Patch coverage: 100.00% of modified lines in pull request are covered.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #2529      +/-   ##
==========================================
+ Coverage   97.27%   97.28%   +0.01%     
==========================================
  Files          24       24              
  Lines        4178     4199      +21     
  Branches      384      384              
==========================================
+ Hits         4064     4085      +21     
  Misses        112      112              
  Partials        2        2              
```


| [Impacted Files](https://codecov.io/gh/anuraghazra/github-readme-stats/pull/2529?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Anurag+Hazra) | Coverage Δ | |
|---|---|---|
| [themes/index.js](https://codecov.io/gh/anuraghazra/github-readme-stats/pull/2529?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Anurag+Hazra#diff-dGhlbWVzL2luZGV4Lmpz) | `100.00% <100.00%> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Anurag+Hazra). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Anurag+Hazra)

</details>

[:umbrella: View full report at Codecov](https://codecov.io/gh/anuraghazra/github-readme-stats/pull/2529?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Anurag+Hazra).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Anurag+Hazra).

Zo-Bro-23: > All 3 themes also have transparent backgrounds that will show differently per-user via GiHub's own light and dark themes.

This didn't seem to be the case, so I made the backgrounds transparent (376eb45). Feel free to change the background to a more opaque translucent color if you want to!

As for accessibility, the second and third themes seem to be good to go :+1: The first one, however, seems to have a low contrast in both your version and the new transparent version. I'm colorblind, so that might be why. Please update that theme to have a slightly higher contrast and I will approve 🎉

PS: Having font and icon colors with a slightly higher contrast for all the themes might also be a good idea. Being accessible-friendly is never a bad thing!

Patch:
diff --git a/.github/labeler.yml b/.github/labeler.yml
index be97765..fad3eee 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -1,3 +1,4 @@
 themes: themes/index.js
 doc-translation: docs/*
 card-i18n: src/translations.js
+documentation: readme.md
diff --git a/.github/workflows/generate-theme-doc.yml b/.github/workflows/generate-theme-doc.yml
index d5fac06..75f6511 100644
--- a/.github/workflows/generate-theme-doc.yml
+++ b/.github/workflows/generate-theme-doc.yml
@@ -23,6 +23,10 @@ jobs:
           node-version: ${{ matrix.node-version }}
           cache: npm
 
+      # Fix the unsafe repo error which was introduced by the CVE-2022-24765 git patches.
+      - name: Fix unsafe repo error
+        run: git config --global --add safe.directory ${{ github.workspace }}
+
       - name: npm install, generate readme
         run: |
           npm ci
diff --git a/.gitignore b/.gitignore
index 2cdfa3d..2501750 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,5 @@ vercel_token
 # IDE
 .vscode
 *.code-workspace
+
+.vercel
diff --git a/api/status/pat-info.js b/api/status/pat-info.js
index 775e068..69d869e 100644
--- a/api/status/pat-info.js
+++ b/api/status/pat-info.js
@@ -2,11 +2,11 @@
  * @file Contains a simple cloud function that can be used to check which PATs are no
  * longer working. It returns a list of valid PATs, expired PATs and PATs with errors.
  *
- * @description This function is currently rate limited to 1 request per 10 minutes.
+ * @description This function is currently rate limited to 1 request per 5 minutes.
  */
 
 import { logger, request, dateDiff } from "../../src/common/utils.js";
-export const RATE_LIMIT_SECONDS = 60 * 5; // 1 request per 10 minutes
+export const RATE_LIMIT_SECONDS = 60 * 5; // 1 request per 5 minutes
 
 /**
  * Simple uptime check fetcher for the PATs.
diff --git a/api/status/up.js b/api/status/up.js
index 33fe8f9..678a20b 100644
--- a/api/status/up.js
+++ b/api/status/up.js
@@ -2,13 +2,13 @@
  * @file Contains a simple cloud function that can be used to check if the PATs are still
  * functional.
  *
- * @description This function is currently rate limited to 1 request per 10 minutes.
+ * @description This function is currently rate limited to 1 request per 5 minutes.
  */
 
 import retryer from "../../src/common/retryer.js";
 import { logger, request } from "../../src/common/utils.js";
 
-export const RATE_LIMIT_SECONDS = 60 * 10; // 1 request per 10 minutes
+export const RATE_LIMIT_SECONDS = 60 * 5; // 1 request per 5 minutes
 
 /**
  * Simple uptime check fetcher for the PATs.
diff --git a/express.js b/express.js
new file mode 100644
index 0000000..6ce92ff
--- /dev/null
+++ b/express.js
@@ -0,0 +1,15 @@
+import statsCard from "./api/index.js";
+import repoCard from "./api/pin.js";
+import langCard from "./api/top-langs.js";
+import wakatimeCard from "./api/wakatime.js";
+import express from "express";
+import dotenv from "dotenv";
+
+dotenv.config();
+const app = express();
+app.listen(process.env.port || 9000);
+
+app.get("/", statsCard);
+app.get("/pin", repoCard);
+app.get("/top-langs", langCard);
+app.get("/wakatime", wakatimeCard);
diff --git a/readme.md b/readme.md
index 29852bf..7a60228 100644
--- a/readme.md
+++ b/readme.md
@@ -92,7 +92,9 @@ Visit <https://indiafightscorona.giveindia.org> and make a small donation to hel
     -   [Repo Card Exclusive Options](#repo-card-exclusive-options)
     -   [Language Card Exclusive Options](#language-card-exclusive-options)
     -   [Wakatime Card Exclusive Option](#wakatime-card-exclusive-options)
--   [Deploy Yourself](#deploy-on-your-own-vercel-instance)
+-   [Deploy Yourself](#deploy-on-your-own)
+    -   [On Vercel](#on-vercel)
+    -   [On other platforms](#on-other-platforms)
     -   [Keep your fork up to date](#keep-your-fork-up-to-date)
 
 # GitHub Stats Card
@@ -133,7 +135,7 @@ You can add the count of all your private contributions to the total commits cou
 
 ### Showing icons
 
-To enable icons, you can pass `show_icons=true` in the query param, like so:
+To enable icons, you can pass `&show_icons=true` in the query param, like so:
 
 ```md
 ![Anurag's GitHub stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true)
@@ -509,7 +511,9 @@ By default, GitHub does not lay out the cards side by side. To do that, you can
 </a>
 ```
 
-## Deploy on your own Vercel instance
+## Deploy on your own
+
+### On Vercel
 
 #### :film_projector: [Check Out Step By Step Video Tutorial By @codeSTACKr](https://youtu.be/n6d4KHSKqGk?t=107)
 
@@ -546,6 +550,23 @@ Since the GitHub API only allows 5k requests per hour, my `https://github-readme
 
 </details>
 
+### On other platforms
+
+> **Warning**
+> This way of using GRS is not officially supported and was added to cater to some particular use cases where Vercel could not be used (e.g. #2341). The support for this method, therefore, is limited.
+
+<details>
+<summary><b>:hammer_and_wrench: Step-by-step guide for deploying on other platforms</b></summary>
+
+1. Fork or clone this repo as per your needs
+2. Add `express` to the dependencies section of `package.json`
+https://github.com/anuraghazra/github-readme-stats/blob/ba7c2f8b55eac8452e479c8bd38b044d204d0424/package.json#L54-L61
+3. Run `npm i` if needed (initial setup)
+4. Run `node express.js` to start the server, or set the entry point to `express.js` in `package.json` if you're deploying on a managed service
+https://github.com/anuraghazra/github-readme-stats/blob/ba7c2f8b55eac8452e479c8bd38b044d204d0424/package.json#L11
+5. You're done 🎉
+</details>
+
 ### Keep your fork up to date
 
 You can keep your fork, and thus your private Vercel instance up to date with the upstream using GitHubs' [Sync Fork button](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork). You can also use the [pull](https://github.com/wei/pull) package created by [@wei](https://github.com/wei) to automate this process.
diff --git a/scripts/preview-theme.js b/scripts/preview-theme.js
index 38faf87..e18c01b 100644
--- a/scripts/preview-theme.js
+++ b/scripts/preview-theme.js
@@ -43,6 +43,23 @@ const ACCEPTED_COLOR_PROPS = Object.keys(COLOR_PROPS);
 const REQUIRED_COLOR_PROPS = ACCEPTED_COLOR_PROPS.slice(0, 4);
 const INVALID_REVIEW_COMMENT = (commentUrl) =>
   `Some themes are invalid. See the [Automated Theme Preview](${commentUrl}) comment above for more information.`;
+var OCTOKIT;
+var OWNER;
+var REPO;
+var PULL_REQUEST_ID;
+
+/**
+ * Incorrect JSON format error.
+ * @extends Error
+ * @param {string} message Error message.
+ * @returns {Error} IncorrectJsonFormatError.
+ */
+class IncorrectJsonFormatError extends Error {
+  constructor(message) {
+    super(message);
+    this.name = "IncorrectJsonFormatError";
+  }
+}
 
 /**
  * Retrieve PR number from the event payload.
@@ -126,15 +143,36 @@ const findComment = async (octokit, issueNumber, owner, repo, commenter) => {
  * Create or update the preview comment.
  *
  * @param {Object} octokit Octokit instance.
- * @param {Object} props Comment properties.
+ * @param {number} issueNumber Issue number.
+ * @param {Object} repo Repository name.
+ * @param {Object} owner Owner of the repository.
+ * @param {number} commentId Comment ID.
+ * @param {string} body Comment body.
  * @return {string} The comment URL.
  */
-const upsertComment = async (octokit, props) => {
+const upsertComment = async (
+  octokit,
+  issueNumber,
+  repo,
+  owner,
+  commentId,
+  body,
+) => {
   let resp;
-  if (props.comment_id !== undefined) {
-    resp = await octokit.issues.updateComment(props);
+  if (commentId !== undefined) {
+    resp = await octokit.issues.updateComment({
+      owner,
+      repo,
+      comment_id: commentId,
+      body,
+    });
   } else {
-    resp = await octokit.issues.createComment(props);
+    resp = await octokit.issues.createComment({
+      owner,
+      repo,
+      issue_number: issueNumber,
+      body,
+    });
   }
   return resp.data.html_url;
 };
@@ -269,22 +307,38 @@ const parseJSON = (json) => {
     if (typeof parsedJson === "object") {
       return parsedJson;
     } else {
-      throw new Error("PR diff is not a valid theme JSON object.");
+      throw new IncorrectJsonFormatError(
+        "PR diff is not a valid theme JSON object.",
+      );
     }
   } catch (error) {
-    let parsedJson = json
+    // Remove trailing commas (if any).
+    let parsedJson = json.replace(/(,\s*})/g, "}");
+
+    // Remove JS comments (if any).
+    parsedJson = parsedJson.replace(/\/\/[A-z\s]*\s/g, "");
+
+    // Fix incorrect open bracket (if any).
+    const splitJson = parsedJson
       .split(/([\s\r\s]*}[\s\r\s]*,[\s\r\s]*)(?=[\w"-]+:)/)
-      .filter((x) => typeof x !== "string" || !!x.trim());
-    if (parsedJson[0].replace(/\s+/g, "") === "},") {
-      parsedJson[0] = "},";
-      if (!/\s*}\s*,?\s*$/.test(parsedJson[1])) {
-        parsedJson.push(parsedJson.shift());
+      .filter((x) => typeof x !== "string" || !!x.trim()); // Split json into array of strings and objects.
+    if (splitJson[0].replace(/\s+/g, "") === "},") {
+      splitJson[0] = "},";
+      if (!/\s*}\s*,?\s*$/.test(splitJson[1])) {
+        splitJson.push(splitJson.shift());
       } else {
-        parsedJson.shift();
+        splitJson.shift();
       }
-      return Hjson.parse(parsedJson.join(""));
-    } else {
-      throw error;
+      parsedJson = splitJson.join("");
+    }
+
+    // Try to parse the fixed json.
+    try {
+      return Hjson.parse(parsedJson);
+    } catch (error) {
+      throw new IncorrectJsonFormatError(
+        `Theme JSON file could not be parsed: ${error.message}`,
+      );
     }
   }
 };
@@ -298,12 +352,13 @@ const themeNameAlreadyExists = (name) => {
   return themes[name] !== undefined;
 };
 
+const DRY_RUN = process.env.DRY_RUN === "true" || false;
+
 /**
  * Main function.
  */
-export const run = async (prNumber) => {
+export const run = async () => {
   try {
-    const dryRun = process.env.DRY_RUN === "true" || false;
     debug("Retrieve action information from context...");
     debug(`Context: ${inspect(github.context)}`);
     let commentBody = `
@@ -311,40 +366,50 @@ export const run = async (prNumber) => {
       \r${THEME_CONTRIB_GUIDELINESS}
     `;
     const ccc = new ColorContrastChecker();
-    const octokit = github.getOctokit(getGithubToken());
-    const pullRequestId = prNumber ? prNumber : getPrNumber();
-    const commenter = getCommenter();
+    OCTOKIT = github.getOctokit(getGithubToken());
+    PULL_REQUEST_ID = getPrNumber();
     const { owner, repo } = getRepoInfo(github.context);
-    debug(`Owner: ${owner}`);
-    debug(`Repo: ${repo}`);
+    OWNER = owner;
+    REPO = repo;
+    const commenter = getCommenter();
+    PULL_REQUEST_ID = getPrNumber();
+    debug(`Owner: ${OWNER}`);
+    debug(`Repo: ${REPO}`);
     debug(`Commenter: ${commenter}`);
 
     // Retrieve the PR diff and preview-theme comment.
     debug("Retrieve PR diff...");
-    const res = await octokit.pulls.get({
-      owner,
-      repo,
-      pull_number: pullRequestId,
+    const res = await OCTOKIT.pulls.get({
+      owner: OWNER,
+      repo: REPO,
+      pull_number: PULL_REQUEST_ID,
       mediaType: {
         format: "diff",
       },
     });
     debug("Retrieve preview-theme comment...");
     const comment = await findComment(
-      octokit,
-      pullRequestId,
-      owner,
-      repo,
+      OCTOKIT,
+      PULL_REQUEST_ID,
+      OWNER,
+      REPO,
       commenter,
     );
 
     // Retrieve theme changes from the PR diff.
     debug("Retrieve themes...");
     const diff = parse(res.data);
+
+    // Retrieve all theme changes from the PR diff and convert to JSON.
+    debug("Retrieve theme changes...");
     const content = diff
       .find((file) => file.to === "themes/index.js")
-      .chunks[0].changes.filter((c) => c.type === "add")
-      .map((c) => c.content.replace("+", ""))
+      .chunks.map((chunk) =>
+        chunk.changes
+          .filter((c) => c.type === "add")
+          .map((c) => c.content.replace("+", ""))
+          .join(""),
+      )
       .join("");
     const themeObject = parseJSON(content);
     if (
@@ -513,14 +578,15 @@ export const run = async (prNumber) => {
     // Create or update theme-preview comment.
     debug("Create or update theme-preview comment...");
     let comment_url;
-    if (!dryRun) {
-      comment_url = await upsertComment(octokit, {
-        comment_id: comment?.id,
-        issue_number: pullRequestId,
-        owner,
-        repo,
-        body: commentBody,
-      });
+    if (!DRY_RUN) {
+      comment_url = await upsertComment(
+        OCTOKIT,
+        PULL_REQUEST_ID,
+        REPO,
+        OWNER,
+        comment?.id,
+        commentBody,
+      );
     } else {
       info(`DRY_RUN: Comment body: ${commentBody}`);
       comment_url = "";
@@ -535,20 +601,20 @@ export const run = async (prNumber) => {
     const reviewReason = themesValid
       ? undefined
       : INVALID_REVIEW_COMMENT(comment_url);
-    if (!dryRun) {
+    if (!DRY_RUN) {
       await addReview(
-        octokit,
-        pullRequestId,
-        owner,
-        repo,
+        OCTOKIT,
+        PULL_REQUEST_ID,
+        OWNER,
+        REPO,
         reviewState,
         reviewReason,
       );
       await addRemoveLabel(
-        octokit,
-        pullRequestId,
-        owner,
-        repo,
+        OCTOKIT,
+        PULL_REQUEST_ID,
+        OWNER,
+        REPO,
         "invalid",
         !themesValid,
       );
@@ -558,20 +624,22 @@ export const run = async (prNumber) => {
     }
   } catch (error) {
     debug("Set review state to `REQUEST_CHANGES` and add `invalid` label...");
-    if (!dryRun) {
+    if (!DRY_RUN) {
       await addReview(
-        octokit,
-        pullRequestId,
-        owner,
-        repo,
+        OCTOKIT,
+        PULL_REQUEST_ID,
+        OWNER,
+        REPO,
         "REQUEST_CHANGES",
-        error.message,
+        "**Something went wrong in the theme preview action:** `" +
+          error.message +
+          "`",
       );
       await addRemoveLabel(
-        octokit,
-        pullRequestId,
-        owner,
-        repo,
+        OCTOKIT,
+        PULL_REQUEST_ID,
+        OWNER,
+        REPO,
         "invalid",
         true,
       );
diff --git a/src/cards/top-languages-card.js b/src/cards/top-languages-card.js
index be1328c..ce8e12a 100644
--- a/src/cards/top-languages-card.js
+++ b/src/cards/top-languages-card.js
@@ -13,7 +13,7 @@ import {
 import { langCardLocales } from "../translations.js";
 
 const DEFAULT_CARD_WIDTH = 300;
-const MIN_CARD_WIDTH = 230;
+const MIN_CARD_WIDTH = 280;
 const DEFAULT_LANGS_COUNT = 5;
 const DEFAULT_LANG_COLOR = "#858585";
 const CARD_PADDING = 25;
@@ -317,7 +317,6 @@ const renderTopLanguages = (topLangs, options = {}) => {
 
   let finalLayout = "";
   if (layout === "compact" || hide_progress == true) {
-    width = width + 50; // padding
     height =
       calculateCompactLayoutHeight(langs.length) + (hide_progress ? -25 : 0);
 
diff --git a/src/cards/wakatime-card.js b/src/cards/wakatime-card.js
index e7af1df..2c32955 100644
--- a/src/cards/wakatime-card.js
+++ b/src/cards/wakatime-card.js
@@ -118,6 +118,7 @@ const createTextNode = ({
         // @ts-ignore
         name: label,
         progressBarBackgroundColor,
+        delay: staggerDelay + 300,
       });
 
   return `
@@ -276,11 +277,12 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
   } else {
     finalLayout = flexLayout({
       items: filteredLanguages.length
-        ? filteredLanguages.map((language) => {
+        ? filteredLanguages.map((language, index) => {
             return createTextNode({
               id: language.name,
               label: language.name,
               value: language.text,
+              index: index,
               percent: language.percent,
               // @ts-ignore
               progressBarColor: titleColor,
@@ -321,7 +323,29 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
   card.setCSS(
     `
     ${cssStyles}
+    @keyframes slideInAnimation {
+      from {
+        width: 0;
+      }
+      to {
+        width: calc(100%-100px);
+      }
+    }
+    @keyframes growWidthAnimation {
+      from {
+        width: 0;
+      }
+      to {
+        width: 100%;
+      }
+    }
     .lang-name { font: 400 11px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${textColor} }
+    #rect-mask rect{
+      animation: slideInAnimation 1s ease-in-out forwards;
+    }
+    .lang-progress{
+      animation: growWidthAnimation 0.6s ease-in-out forwards;
+    }
     `,
   );
 
diff --git a/src/common/languageColors.json b/src/common/languageColors.json
index 47bfb1c..b50cba6 100644
--- a/src/common/languageColors.json
+++ b/src/common/languageColors.json
@@ -79,6 +79,7 @@
     "Ceylon": "#dfa535",
     "Chapel": "#8dc63f",
     "ChucK": "#3f8000",
+    "Circom": "#707575",
     "Cirru": "#ccccff",
     "Clarion": "#db901e",
     "Clarity": "#5546ff",
@@ -116,6 +117,7 @@
     "DirectX 3D File": "#aace60",
     "Dockerfile": "#384d54",
     "Dogescript": "#cca760",
+    "Dotenv": "#e5d559",
     "Dylan": "#6c616e",
     "E": "#ccce35",
     "ECL": "#8a1267",
@@ -149,7 +151,7 @@
     "Forth": "#341708",
     "Fortran": "#4d41b1",
     "Fortran Free Form": "#4d41b1",
-    "FreeBasic": "#867db1",
+    "FreeBasic": "#141AC9",
     "FreeMarker": "#0050b2",
     "Frege": "#00cafe",
     "Futhark": "#5f021f",
@@ -182,6 +184,7 @@
     "Go": "#00ADD8",
     "Go Checksums": "#00ADD8",
     "Go Module": "#00ADD8",
+    "Godot Resource": "#355570",
     "Golo": "#88562A",
     "Gosu": "#82937f",
     "Grace": "#615f8b",
@@ -192,6 +195,7 @@
     "Groovy": "#4298b8",
     "Groovy Server Pages": "#4298b8",
     "HAProxy": "#106da9",
+    "HCL": "#844FBA",
     "HLSL": "#aace60",
     "HOCON": "#9ff8ee",
     "HTML": "#e34c26",
@@ -225,6 +229,7 @@
     "Isabelle ROOT": "#FEFE00",
     "J": "#9EEDFF",
     "JAR Manifest": "#b07219",
+    "JCL": "#d90e09",
     "JFlex": "#DBCA00",
     "JSON": "#292929",
     "JSON with Comments": "#292929",
@@ -247,9 +252,11 @@
     "Jsonnet": "#0064bd",
     "Julia": "#a270ba",
     "Jupyter Notebook": "#DA5B0B",
+    "Just": "#384d54",
     "KRL": "#28430A",
     "Kaitai Struct": "#773b37",
     "KakouneScript": "#6f8042",
+    "KerboScript": "#41adf0",
     "KiCad Layout": "#2f4aab",
     "KiCad Legacy Layout": "#2f4aab",
     "KiCad Schematic": "#2f4aab",
@@ -362,6 +369,7 @@
     "PicoLisp": "#6067af",
     "PigLatin": "#fcd7de",
     "Pike": "#005390",
+    "PlantUML": "#fbbd16",
     "PogoScript": "#d80074",
     "Polar": "#ae81ff",
     "Portugol": "#f8bd00",
@@ -379,6 +387,7 @@
     "Puppet": "#302B6D",
     "PureBasic": "#5a6986",
     "PureScript": "#1D222D",
+    "Pyret": "#ee1e10",
     "Python": "#3572A5",
     "Python console": "#3572A5",
     "Python traceback": "#3572A5",
@@ -531,7 +540,6 @@
     "fish": "#4aae47",
     "hoon": "#00b171",
     "jq": "#c7254e",
-    "just": "#384d54",
     "kvlang": "#1da6e0",
     "mIRC Script": "#3d57c3",
     "mcfunction": "#E22837",
diff --git a/themes/index.js b/themes/index.js
index 60825d1..d348a2b 100644
--- a/themes/index.js
+++ b/themes/index.js
@@ -18,6 +18,27 @@ export const themes = {
     text_color: "417E87",
     bg_color: "ffffff00",
   },
+  shadow_red: {
+    title_color: "9A0000",
+    text_color: "444",
+    icon_color: "4F0000",
+    border_color: "4F0000",
+    bg_color: "ffffff00",
+  },
+  shadow_green: {
+    title_color: "007A00",
+    text_color: "444",
+    icon_color: "003D00",
+    border_color: "003D00",
+    bg_color: "ffffff00",
+  },
+  shadow_blue: {
+    title_color: "00779A",
+    text_color: "444",
+    icon_color: "004450",
+    border_color: "004490",
+    bg_color: "ffffff00",
+  },
   dark: {
     title_color: "fff",
     icon_color: "79ff97",
@@ -374,6 +395,13 @@ export const themes = {
     border_color: "170F0C",
     bg_color: "170F0C",
   },
+  one_dark_pro: {
+    title_color: "61AFEF",
+    text_color: "E5C06E",
+    icon_color: "C678DD",
+    border_color: "3B4048",
+    bg_color: "23272E",
+  },
   rose: {
     title_color: "8d192b",
     text_color: "862931",
@@ -381,6 +409,13 @@ export const themes = {
     border_color: "e9d8d4",
     bg_color: "e9d8d4",
   },
+  holi_theme: {
+    title_color: "5FABEE",
+    text_color: "D6E7FF",
+    icon_color: "5FABEE",
+    border_color: "85A4C0",
+    bg_color: "030314",
+  },
 };
 
 export default themes;


Test patch:
diff --git a/tests/__snapshots__/renderWakatimeCard.test.js.snap b/tests/__snapshots__/renderWakatimeCard.test.js.snap
index 1c0bd70..6dfaf98 100644
--- a/tests/__snapshots__/renderWakatimeCard.test.js.snap
+++ b/tests/__snapshots__/renderWakatimeCard.test.js.snap
@@ -69,7 +69,29 @@ exports[`Test Render Wakatime Card should render correctly with compact layout 1
     }
     
   
+    @keyframes slideInAnimation {
+      from {
+        width: 0;
+      }
+      to {
+        width: calc(100%-100px);
+      }
+    }
+    @keyframes growWidthAnimation {
+      from {
+        width: 0;
+      }
+      to {
+        width: 100%;
+      }
+    }
     .lang-name { font: 400 11px 'Segoe UI', Ubuntu, Sans-Serif; fill: #434d58 }
+    #rect-mask rect{
+      animation: slideInAnimation 1s ease-in-out forwards;
+    }
+    .lang-progress{
+      animation: growWidthAnimation 0.6s ease-in-out forwards;
+    }
     
 
           
@@ -227,7 +249,29 @@ exports[`Test Render Wakatime Card should render correctly with compact layout w
     }
     
   
+    @keyframes slideInAnimation {
+      from {
+        width: 0;
+      }
+      to {
+        width: calc(100%-100px);
+      }
+    }
+    @keyframes growWidthAnimation {
+      from {
+        width: 0;
+      }
+      to {
+        width: 100%;
+      }
+    }
     .lang-name { font: 400 11px 'Segoe UI', Ubuntu, Sans-Serif; fill: #434d58 }
+    #rect-mask rect{
+      animation: slideInAnimation 1s ease-in-out forwards;
+    }
+    .lang-progress{
+      animation: growWidthAnimation 0.6s ease-in-out forwards;
+    }
     
 
           
diff --git a/tests/e2e/e2e.test.js b/tests/e2e/e2e.test.js
index 402e210..f34859d 100644
--- a/tests/e2e/e2e.test.js
+++ b/tests/e2e/e2e.test.js
@@ -15,14 +15,14 @@ const REPO = "curly-fiesta";
 const USER = "catelinemnemosyne";
 const STATS_DATA = {
   name: "Cateline Mnemosyne",
-  totalPRs: 1,
-  totalCommits: 7,
+  totalPRs: 2,
+  totalCommits: 8,
   totalIssues: 1,
   totalStars: 1,
   contributedTo: 1,
   rank: {
     level: "A+",
-    score: 50.893750297869225,
+    score: 50.88831151384285,
   },
 };
 
diff --git a/tests/renderTopLanguages.test.js b/tests/renderTopLanguages.test.js
index 8ae4bbd..de9e21f 100644
--- a/tests/renderTopLanguages.test.js
+++ b/tests/renderTopLanguages.test.js
@@ -216,7 +216,7 @@ describe("Test renderTopLanguages", () => {
     );
     expect(queryAllByTestId(document.body, "lang-progress")[0]).toHaveAttribute(
       "width",
-      "120",
+      "100",
     );
 
     expect(queryAllByTestId(document.body, "lang-name")[1]).toHaveTextContent(
@@ -224,7 +224,7 @@ describe("Test renderTopLanguages", () => {
     );
     expect(queryAllByTestId(document.body, "lang-progress")[1]).toHaveAttribute(
       "width",
-      "120",
+      "100",
     );
 
     expect(queryAllByTestId(document.body, "lang-name")[2]).toHaveTextContent(
@@ -232,7 +232,7 @@ describe("Test renderTopLanguages", () => {
     );
     expect(queryAllByTestId(document.body, "lang-progress")[2]).toHaveAttribute(
       "width",
-      "60",
+      "50",
     );
   });