diff --git a/.gitignore b/.gitignore
index fa863b61be8800cd7bc066216435abe351c59623..bcb51dff50c5f3523fe9abf6024ecf7336513893 100644
--- a/.gitignore
+++ b/.gitignore
@@ -73,5 +73,7 @@ Pipfile.lock
# JavaScript
node_modules
-package.json
package-lock.json
+
+# uv
+uv.lock
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index 3aa88117db8cbc41a13ebfdb6f0c6855087f8b60..0000000000000000000000000000000000000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,58 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2020-2024 TU Wien.
-#
-# Invenio-Theme-TUW is free software; you can redistribute it and/or modify
-# it under the terms of the MIT License; see LICENSE file for more details.
-
-# TODO: Generate this manifest file by running the following commands:
-# (please sort the lines in this file after running below commands)
-#
-# git init
-# git add -A
-# pip install -e .[all]
-# check-manifest -u
-
-# exclude CI/CD stuff
-exclude sonar-project.properties
-exclude .gitlab-ci.yml
-exclude Pipfile
-
-include .git-blame-ignore-revs
-include .dockerignore
-include .editorconfig
-include .tx/config
-prune docs/_build
-recursive-include .github/workflows *.yml
-recursive-include invenio_theme_tuw/translations *.po *.pot *.mo
-
-# added by check-manifest
-include *.rst
-include *.sh
-include *.txt
-include LICENSE
-include babel.ini
-include pytest.ini
-recursive-include docs *.bat
-recursive-include docs *.py
-recursive-include docs *.rst
-recursive-include docs *.txt
-recursive-include docs Makefile
-recursive-include tests *.py
-recursive-include invenio_theme_tuw *.js
-recursive-include invenio_theme_tuw *.html
-recursive-include invenio_theme_tuw *.jinja
-recursive-include invenio_theme_tuw *.less
-recursive-include invenio_theme_tuw *.pdf
-recursive-include invenio_theme_tuw *.png
-recursive-include invenio_theme_tuw *.webp
-recursive-include invenio_theme_tuw *.svg
-recursive-include invenio_theme_tuw *.avif
-recursive-include invenio_theme_tuw *.jpg
-recursive-include invenio_theme_tuw *.ttf
-recursive-include invenio_theme_tuw *.overrides
-recursive-include invenio_theme_tuw *.variables
-recursive-include invenio_theme_tuw *.babelrc
-recursive-include invenio_theme_tuw *.eslintignore
-recursive-include invenio_theme_tuw *.json
-recursive-include invenio_theme_tuw *.patch
diff --git a/pyproject.toml b/pyproject.toml
index f00e77850c68796aadd5f1c8450816553222feea..4fbc5a49d7bf524b6a605296aca175edef358a55 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,4 +1,90 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2024 TU Wien.
+#
+# Invenio-Theme-TUW is free software; you can redistribute it and/or modify
+# it under the terms of the MIT License; see LICENSE file for more details.
+
[build-system]
-requires = ["setuptools", "wheel", "babel>2.8"]
-build-backend = "setuptools.build_meta"
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+[tool.hatch.version]
+path = "invenio_theme_tuw/__init__.py"
+
+[tool.hatch.build.targets.sdist]
+exclude = [
+ "sonar-project.properties",
+ ".gitlab-ci.yml",
+]
+
+[project]
+name = "invenio-theme-tuw"
+dynamic = ["version"]
+description = "TU Wien corporate design for InvenioRDM."
+keywords = ["invenio", "theme", "corporate design", "tu wien"]
+requires-python = ">=3.9"
+readme = "README.rst"
+license = "MIT"
+authors = [
+ {name = "Team of the Center for Research Data Management (TU Wien)", email = "tudata@tuwien.ac.at"},
+]
+classifiers = [
+ "Environment :: Web Environment",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: OS Independent",
+ "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Development Status :: 5 - Production/Stable",
+]
+dependencies = [
+ "Flask-WebpackExt>=1.0.0",
+ "Invenio-App-RDM>=12.0.0,<13.0.0",
+ "Invenio-Search>=2.0.0",
+ "Flask>=2.0.2",
+ "Flask-Session-Captcha>=1.3.0,<1.4",
+]
+
+[project.optional-dependencies]
+tests = [
+ "pytest-ruff>=0.4.1",
+ "pytest-black>=0.3.0",
+ "pytest-invenio>=1.4.0",
+ "opensearch-dsl>=2.1.0",
+]
+
+[project.urls]
+Repository = "https://gitlab.tuwien.ac.at/crdm/invenio-theme-tuw"
+
+# entrypoints
+[project.entry-points."invenio_base"]
+invenio_theme_tuw = "invenio_theme_tuw:InvenioThemeTUW"
+
+[project.entry-points."invenio_base.blueprints"]
+invenio_theme_tuw_blueprint = "invenio_theme_tuw.views:create_blueprint"
+
+[project.entry-points."invenio_base.finalize_app"]
+invenio_theme_tuw_bp_order = "invenio_theme_tuw.startup:override_bp_order"
+invenio_theme_tuw_deposit_pages = "invenio_theme_tuw.startup:override_deposit_pages"
+
+[project.entry-points."invenio_assets.webpack"]
+invenio_theme_tuw_theme = "invenio_theme_tuw.webpack:theme"
+
+[project.entry-points."invenio_config.module"]
+invenio_theme_tuw = "invenio_theme_tuw.config"
+
+# configuration for tools
+[tool.isort]
+profile = "black"
+
+[tool.pytest.ini_options]
+addopts = '--ruff --black --isort --pydocstyle --doctest-glob="*.rst" --doctest-modules --cov=invenio_theme_tuw --cov-report=term-missing'
+testpaths = "tests invenio_theme_tuw"
+[tool.pydocstyle]
+add_ignore = "D401"
diff --git a/run-tests.sh b/run-tests.sh
index fb16fa22ea2f20494b9fd803c77ff379a4c27a88..7a32d245599aa0b47a574a66a42e4a227d896237 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -40,11 +40,10 @@ if [[ ${keep_services} -eq 0 ]]; then
fi
export LC_TIME=en_US.UTF-8
-python -m check_manifest
python -m sphinx.cmd.build -qnNW docs docs/_build/html
eval "$(docker-services-cli up --db "${DB:-postgresql}" --search "${SEARCH:-opensearch}" --mq "${MQ:-rabbitmq}" --cache "${CACHE:-redis}" --env)"
# Note: expansion of pytest_args looks like below to not cause an unbound
# variable error when 1) "nounset" and 2) the array is empty.
-python -m pytest ${pytest_args[@]+"${pytest_args[@]}"}
+pytest ${pytest_args[@]+"${pytest_args[@]}"}
tests_exit_code=$?
exit "$tests_exit_code"
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index c27598aa6e0b5716d63dbcd86fffb8ccdd34bfea..0000000000000000000000000000000000000000
--- a/setup.cfg
+++ /dev/null
@@ -1,110 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2020-2024 TU Wien.
-#
-# Invenio-Theme-TUW is free software; you can redistribute it and/or modify
-# it under the terms of the MIT License; see LICENSE file for more details.
-
-[metadata]
-name = invenio-theme-tuw
-version = attr: invenio_theme_tuw.__version__
-description = "TU Wien theme for Invenio (RDM)."
-long_description = file: README.rst, CHANGES.rst
-keywords = invenio theme tuw
-license = MIT
-author = TU Wien
-author_email = tudata@tuwien.ac.at
-platforms = any
-url = https://gitlab.tuwien.ac.at/fairdata/invenio-theme-tuw
-classifiers =
- Environment :: Web Environment
- License :: OSI Approved :: MIT License
- Operating System :: OS Independent
- Topic :: Internet :: WWW/HTTP :: Dynamic Content
- Topic :: Software Development :: Libraries :: Python Modules
- Programming Language :: Python
- Programming Language :: Python :: 3.8
- Programming Language :: Python :: 3.9
- Development Status :: 5 - Production/Stable
-
-[options]
-include_package_data = True
-packages = find:
-python_requires = >=3.7
-zip_safe = False
-install_requires =
- Flask-WebpackExt>=1.0.0
- Invenio-App-RDM>=12.0.0rc2,<13.0.0
- Invenio-Search>=2.0.0
- Flask>=2.0.2
- Flask-Session-Captcha>=1.3.0,<1.4
-
-[options.extras_require]
-tests =
- pytest-black>=0.3.0
- pytest-invenio>=1.4.0
- sphinx>=4.5
- opensearch-dsl>=2.1.0
-# Kept for backwards compatibility
-docs =
-
-[options.entry_points]
-invenio_base.apps =
- invenio_theme_tuw = invenio_theme_tuw:InvenioThemeTUW
-invenio_base.blueprints =
- invenio_theme_tuw_blueprint = invenio_theme_tuw.views:create_blueprint
-invenio_base.finalize_app =
- invenio_theme_tuw_bp_order = invenio_theme_tuw.startup:override_bp_order
- invenoi_theme_tuw_deposit_pages = invenio_theme_tuw.startup:override_deposit_pages
-invenio_assets.webpack =
- invenio_theme_tuw_theme = invenio_theme_tuw.webpack:theme
-invenio_config.module =
- invenio_theme_tuw = invenio_theme_tuw.config
-
-[build_sphinx]
-source-dir = docs/
-build-dir = docs/_build
-all_files = 1
-
-[bdist_wheel]
-universal = 1
-
-[pydocstyle]
-add_ignore = D401
-
-[pycodestyle]
-exclude = docs/conf.py
-ignore = E203,E501,W503
-
-[compile_catalog]
-directory = invenio_theme_tuw/translations/
-
-[extract_messages]
-copyright_holder = TU Wien
-msgid_bugs_address = tudata@tuwien.ac.at
-mapping-file = babel.ini
-output-file = invenio_theme_tuw/translations/messages.pot
-add-comments = NOTE
-
-[init_catalog]
-input-file = invenio_theme_tuw/translations/messages.pot
-output-dir = invenio_theme_tuw/translations/
-
-[update_catalog]
-input-file = invenio_theme_tuw/translations/messages.pot
-output-dir = invenio_theme_tuw/translations/
-
-[flake8]
-max-line-length = 88
-extend-ignore = E203
-
-[isort]
-profile=black
-
-[check-manifest]
-ignore =
- *-requirements.txt
-
-[tool:pytest]
-addopts = --black --isort --pydocstyle --doctest-glob="*.rst" --doctest-modules --cov=invenio_theme_tuw --cov-report=term-missing
-testpaths = docs tests invenio_theme_tuw
diff --git a/setup.py b/setup.py
deleted file mode 100644
index ca337356365df2ec7d2184ea5766b9a9a9aa605c..0000000000000000000000000000000000000000
--- a/setup.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2020-2022 TU Wien.
-#
-# Invenio-Theme-TUW is free software; you can redistribute it and/or modify
-# it under the terms of the MIT License; see LICENSE file for more details.
-
-"""TU Wien theme for Invenio (RDM)."""
-
-from setuptools import setup
-
-setup()