How to create a Custom theme for TeamSpeak 6

Official Documentations

You can either find the Documentation in the Client beside the theme selector, or just open up these spoilers:

Official theme documentation from the TeamSpeak Client

TeamSpeak Extension Information

This file aims to provide information for extension authors to build extensions
for the TeamSpeak client.

Note: This is currently alpha state and subject to change.
This also applies to this documentation, which is probably not as thorough as
you might want.
Please report issues and shortcomings of this documentation
on the usual channels.

Please note that remote content is not allowed to be loaded or accessed
by extensions.

General information

Extensions consist of a folder that must contain all the files necessary for
your extension to work properly.
This folder needs to contain a package.json file that must follow a specific
format.
The format and content of the package.json is verified to match the
extensions.json schema that accompanies this
documentation. You may use a validator
to verify that your package.json is valid. The client will also log validation
failures in the log.
In the future the json schema will also be available online (location TBD).

Naming conventions

The folder name of your extension is supposed to follow standard identifier naming.
For example com.teamspeak.dark or com.teamspeak.themes.dark
would be reasonable identifiers for the default Dark theme included in the client.
The folder name must match your extension identifier in the package.json, and both
must be all lowercase.

Package Information File

The package.json contains information about your extension, such as the content of
the extension, a unique identifier, a public repository containing the source code
and author information. It may optionally contain additional links to community and
support information, as well as a TeamSpeak server address and a public chat room.

Themes

Themes consists of:

  • One CSS file, containing all the CSS rules you wish to apply to the client
  • One image file, showing a preview of your Theme displayed in the client
  • Additional files you require (such as images)

An extension may optionally contain multiple variations of the same theme, if you
wish to provide several different versions (with different accent colors for
example).
However if your themes are vastly different we would strongly suggest to package
them as separate extensions instead.

Example

A sort of minimal example package.json might look as follows:

{
    "name": "Example Theme",
    "description": "This is an example theme, making everything purple",
    "version": "1.0.0",
    "identifier": "invalid.example.theme",
    "engines": {
        "teamspeak": 1
    },
    "repository": {
        "url": "https://github.com/bestDudeEver/themes/",
        "directory": "purple",
        "type": "git"
    },
    "image": "preview.png",
    "license": "MIT",
    "author": {
        "name": "Joe Doe",
        "email": "[email protected]",
        "url": "https://example.net",
        "userTag": "[email protected]"
    },
    "content": {
        "themes": [
            {
                "name": "Purple Theme",
                "source": "purple.css",
                "image": "purple-preview.png",
                "apiVersion": 1
            }
        ]
    }
}

Please note that the repository could also just be the link itself, if you don’t
need the other information.
Similarly the author can just be "author": "Joe Doe" in case you don’t want to
provide the additional details.

JSON scheme to validate the package.json
{
  "$schema": "https://json-schema.org/draft-07/schema",
  "$id": "https://teamspeak.com/specs/extensions.scheme.json",
  "title": "TeamSpeak Extension",
  "description": "A TeamSpeak Extension meta data file for the TeamSpeak client",
  "type": "object",
  "additionalProperties": false,
  "required": [ "engines", "identifier", "image", "repository", "author", "description", "name", "version", "content", "license" ],
  "properties": {
    "engines": {
      "type": "object",
      "required": [ "teamspeak" ],
      "minProperties": 1,
      "maxProperties": 1,
      "additionalProperties": false,
      "properties": {
        "teamspeak": {
          "description": "The version of the extension meta data information specification this file adheres to",
          "type": "integer",
          "minimum": 1
        }
      }
    },
    "version": {
      "description": "The version of this extension. Use SemVer versioning, see https://semver.org",
      "type": "string",
      "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"
    },
    "name": {
      "description": "The name of this extension as displayed in the UI",
      "type": "string",
      "maxLength": 64,
      "minLength": 5
    },
    "description": {
      "description": "A short concise description of what this extension does",
      "type": "string",
      "maxLength": 256,
      "minLength": 20
    },
    "author": {
      "description": "Information about the author",
      "oneOf": [
        {
          "type": "object",
          "additionalProperties": false,
          "required": [ "name" ],
          "properties": {
            "name": {
              "description": "Name of the author",
              "type": "string",
              "maxLength": 64,
              "minLength": 3
            },
            "email": {
              "description": "Contact email address of the author",
              "type": "string",
              "format": "email"
            },
            "url": {
              "description": "A link to a website to a website of the author",
              "type": "string",
              "pattern": "^https://.*\\..*"
            },
            "userTag": {
              "description": "Public chat alias of the author. See Settings -> Chat -> User Tags",
              "type": "string",
              "format": "email"
            }
          }
        },
        {
          "type": "string",
          "maxLength": 64,
          "minLength": 3
        }
      ]
    },
    "homepage": {
      "description": "A link where further information can be found about this extension",
      "type": "string",
      "maxLength": 1024,
      "pattern": "^https://.*\\..*"
    },
    "docsUrl": {
      "description": "An optional link where documentation for this extension can be found",
      "type": "string",
      "maxLength": 1024,
      "pattern": "^https://.*\\..*"
    },
    "repository": {
      "description": "The repository information where the source code for this extension can be found",
      "oneOf": [
        {
          "type": "object",
          "additionalProperties": false,
          "required": [ "type", "url" ],
          "properties": {
            "type": {
              "description": "The type of the repository (e.g. git) ",
              "type": "string",
              "enum": [ "git", "svn" ]
            },
            "url": {
              "description": "The url of a public git repository that contains the source code for this extension",
              "type": "string",
              "maxLength": 1024,
              "pattern": "^https://.*\\..*"
            },
            "directory": {
              "description": "Optional sub directory within the git repository specified that contains the source code, if the repository contains more than just the source code for this extension",
              "type": "string",
              "maxLength": 256
            }
          }
        },
        {
          "type": "string",
          "pattern": "^https://.*\\..*",
          "minLength": 15,
          "maxLength": 512
        }
      ]
    },
    "community": {
      "description": "Optional links to the support / community for this extension",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "room": {
          "description": "An optional public room alias, which people can join to discuss or get help with this extension",
          "type": "string",
          "pattern": "^#.*:([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])(\\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]))*$",
          "maxLength": 256,
          "minLength": 20
        },
        "server": {
          "description": "An optional teamspeak server address, which people can join to discuss or get help with this extension",
          "type": "string",
          "format": "hostname",
          "maxLength": 128,
          "minLength": 3
        }
      }
    },
    "identifier": {
      "description": "A unique identifier for this extension",
      "type": "string",
      "pattern": "^[a-z]{2,}\\.([a-z]{3,}\\.){1,}[a-z]{3,}$",
      "maxLength": 64,
      "minLength": 11
    },
    "image": {
      "description": "File name of an image to display for this extension",
      "type": "string",
      "minLength": 6
    },
    "license": {
      "description": "The license this work falls under",
      "type": "string",
      "maxLength": 20,
      "minLength": 3
    },
    "keywords": {
      "description": "Short concise keywords describing the extension",
      "type": "array",
      "items": {
        "type": "string",
        "maxLength": 10,
        "minLength": 3
      },
      "maxItems": 5,
      "uniqueItems": true
    },
    "content": {
      "description": "Describes the content of this extension",
      "type": "object",
      "minProperties": 1,
      "additionalProperties": false,
      "properties": {
        "themes": {
          "description": "Information about the theme part of the extension",
          "type": "array",
          "minItems": 1,
          "maxItems": 20,
          "uniqueItems": true,
          "items": {
            "type": "object",
            "required": [ "name", "source", "apiVersion", "image" ],
            "additionalProperties": false,
            "properties": {
              "name": {
                "description": "The name of this theme variant for UI display",
                "type": "string",
                "minLength": 4,
                "maxLength": 30
              },
              "source": {
                "description": "The source file to load for this theme",
                "type": "string",
                "pattern": "^[-\\w\\s=\\.]{3,200}$"
              },
              "apiVersion": {
                "description": "The theme API version this theme implements. Future client releases may choose to not load themes that have an incompatible API version",
                "type": "integer",
                "minimum": 1
              },
              "image": {
                "description": "A small preview image of this theme variant",
                "type": "string",
                "pattern": "^.*\\.(?:png|svg)$",
                "maxLength": 128,
                "minLength": 6
              }
            }
          }
        }
      }
    }
  }
}

How to start?

I’ll recommend looking at other themes first before doing one from scratch. On those you can start making modifications or start all over with a template. They can be found here: ts6-theme


How to access the CSS to modify / Open the element inspector

You need to start your TeamSpeak Client with these parameters or add them to the desktop shortcut

Windows:

--remote-debugging-port=9988 --remote-allow-origins=*

macOS:
If you’re in the application folder, you can use this code to open the client

open TeamSpeak.app --args \
--remote-debugging-port=9988 \
--remote-allow-origins="*"

Then head over to this this site (Make sure to use a Chromium browser, otherwise you’ll not be able to access this feature): about://inspect/#devices

Tick these boxes and add http://localhost:9988 to the target discovery and after a bit of waiting (up to 5 minutes) the client should show up. Click on Inspect and from there on you can view and copy the necessary CSS components.

:information_source: You might need to use inspect fallback if the view is unresponsive.

:information_source: All changes does here are not permanent! They are only available for this session.


Recommended resolutions for images

The image type can be an png or svg. The best resolution that fits the frame of the window is 1024x768 px.

6 Likes

sorry for the late response but I wanted to thank you :smiling_face_with_tear:

3 Likes

is this still working with TS6? Didn’t get it working …

with TS6 it seems to work via this link
localhost:[port]/devtools/inspector.html/?ws=localhost:[port]/devtools/page/[id]

3 Likes

Hey m8 can you tell me how you edit the style?
the

--remote-debugging-port=9222

way is not working with the new TS6 update on my end even with chrome as browser. :confused:

I’ve recently updated my post here.

3 Likes

What’s the right command to start the TS6 client on mac with these args? I’ve tried a number of different variations and they either don’t work, or show some kind of error. This is what I’ve tried:

open -n /Applications/TeamSpeak.app (just as a sanity check)

and

open /Applications/TeamSpeak.app --args --remote-debugging-port=9988 --remote-allow-origins=*

produced this error:
The application cannot be opened for an unexpected reason, error=Error Domain=RBSRequestErrorDomain Code=5 “Launch failed.” UserInfo={NSLocalizedFailureReason=Launch failed., NSUnderlyingError=0x732c186f0 {Error Domain=NSPOSIXErrorDomain Code=162 “Unknown error: 162” UserInfo={NSLocalizedDescription=Launchd job spawn failed}}}

/Applications/TeamSpeak.app --remote-debugging-port=9988 --remote-allow-origins=*

produces this error:
zsh: no matches found: --remote-allow-origins=*

I’ve updated the post to also include a guide for macOS, please give it a try

2 Likes

The new Mac command appears to work, but the link in the next step doesn’t:

image

If I try to manually open the About TeamSpeak from the main menu, nothing happens:

image

I also don’t see anything seemingly relevant in the about section in the client. I know if this is relevant, but as a software engineer I know better than to assume. I’m running the TS6 client, and I am not self-hosted.

Are you opening the website inside of a browser? You may need to use chrome as it is a CEF feature that wouldn’t be available on other Browsers.

2 Likes

I was using FireFox. Downloading Chrome let me get to the screen. It probably would be useful to update the post to include a note about Chrome. I’ll keep working through this and let you know if I run into any additional issues. Thanks for your help.

1 Like

Hi @LeonMarcelHD ,

First, I’m sure you’re slammed under the recent wave of people so no rush. I’m trying to test a custom theme, but when I add it to the extensions folder I’m seeing an error in the console. Can you tell me what I’m doing wrong here? I was able to successfully download and install the public theme, so it’s something with my setup.

{
    "name": "My TS Theme",
    "description": "Tiny banners are sad",
    "version": "1.0.0",
    "identifier": "mytheme.themes.dark",
    "engines": {
        "teamspeak": 1
    },
    "repository": {
        "url": "https://github.com/[myGithub]/[RepoName]",
        "directory": "myteamspeaktheme",
        "type": "git"
    },
    "image": "screenshot.png",
    "license": "MIT",
    "author": {
        "name": "Joe Doe"
    },
    "content": {
        "themes": [
            {
                "name": "My Teamspeak",
                "source": "teamspeak.css",
                "image": "screenshot.png",
                "apiVersion": 1
            }
        ]
    }
}

Here’s my CSS:

:root {
    --tsv-icon-scale: 1.25;
}

.ts-server-header .tsv-view-banner .tsv-view-accessory {
    margin-top: calc((var(--total-banner-height) - var(--tsv-icon-size-xlarge)) / 1);
}

/* This will increase the height of the banner to show more content */
.ts-server-header .tsv-view-banner, .ts-account-settings-view .tsv-view-banner {
    --total-banner-height: 200px !important;
}

/* This will make the background image stand out more with less shadowing */
.tsv-view-background::after {
    opacity: 0.2 !important;
    /* DEFAULT: 0.9 */
}

Here’s the extensions folder I’ve put everything in:

If it’s unclear what the problem is, here’s some additional info.

This is the error I’m seeing in the console when I click the “refresh” button on custom themes:

When I click on the 1st link, it takes me here:

The validator says my package.json is correct:

The Theme works fine on my end, does it also work on your system?

Many errors in the browser inspector can be ignored.

I’ve reached out via DM and the issue is now resolved. Somehow it was working when using the theme I’ve sent to the user.

3 Likes

It does and doesn’t.

When I only include my custom theme in the directory it doesn’t pickup on the fact that it’s in there:

However, if I work around the problem by putting my .css file into the theme that’s linked in the main repo, and add it to the resources list, I am able to select the theme from the list:

What’s unclear is why does my package not successfully picked up by the app.