diff --git a/arkindex/documents/migrations/0007_elementtype_color.py b/arkindex/documents/migrations/0007_elementtype_color.py
new file mode 100644
index 0000000000000000000000000000000000000000..8e1bd4578b864a7f2015d2a22dff394e10861825
--- /dev/null
+++ b/arkindex/documents/migrations/0007_elementtype_color.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.1.7 on 2023-07-06 14:44
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('documents', '0006_index_cleanup'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='elementtype',
+            name='color',
+            field=models.CharField(default='28b62c', max_length=6),
+        ),
+    ]
diff --git a/arkindex/documents/models.py b/arkindex/documents/models.py
index a497f2815233ea726e830b643cb92f6f028cac7c..0ddd5d524c072f9894c3921725273e72d2f2f1c0 100644
--- a/arkindex/documents/models.py
+++ b/arkindex/documents/models.py
@@ -78,6 +78,7 @@ class ElementType(models.Model):
     display_name = models.CharField(max_length=250)
     folder = models.BooleanField(default=False)
     indexable = models.BooleanField(default=False)
+    color = models.CharField(max_length=6, default='28b62c')
 
     class Meta:
         constraints = [
diff --git a/arkindex/documents/serializers/light.py b/arkindex/documents/serializers/light.py
index 999c55bbe46394a6ea857b4317ccff833611d418..00a8b3d4b8d2b5def6300eac3d257b3f7955aa64 100644
--- a/arkindex/documents/serializers/light.py
+++ b/arkindex/documents/serializers/light.py
@@ -80,6 +80,7 @@ class ElementTypeLightSerializer(serializers.ModelSerializer):
             'slug',
             'display_name',
             'folder',
+            'color',
         )
 
     def validate(self, data):
diff --git a/arkindex/documents/tests/test_corpus.py b/arkindex/documents/tests/test_corpus.py
index 00402a92e00444329b4157f0af71895aebe7a3a1..6af0b9be0b10674812bdc49e2be3105f1f985dfe 100644
--- a/arkindex/documents/tests/test_corpus.py
+++ b/arkindex/documents/tests/test_corpus.py
@@ -20,31 +20,37 @@ EXPECTED_CORPUS_TYPES = [
         'slug': 'volume',
         'display_name': 'Volume',
         'folder': True,
+        'color': '28b62c'
     },
     {
         'slug': 'page',
         'display_name': 'Page',
         'folder': False,
+        'color': '28b62c'
     },
     {
         'slug': 'act',
         'display_name': 'Act',
         'folder': False,
+        'color': '28b62c'
     },
     {
         'slug': 'surface',
         'display_name': 'Surface',
         'folder': False,
+        'color': '28b62c'
     },
     {
         'slug': 'text_line',
         'display_name': 'Line',
         'folder': False,
+        'color': '28b62c'
     },
     {
         'slug': 'word',
         'display_name': 'Word',
         'folder': False,
+        'color': '28b62c'
     }
 ]
 
@@ -295,6 +301,7 @@ class TestCorpus(FixtureAPITestCase):
                 'slug',
                 'display_name',
                 'folder',
+                'color',
             )),
             [{
                 'folder': False,
diff --git a/arkindex/documents/tests/test_element_type.py b/arkindex/documents/tests/test_element_type.py
index be7589fe305c2cf21963fe44139e6ca7d9160b65..98e344afa16b3ffd606d8d323411af7ff83522c5 100644
--- a/arkindex/documents/tests/test_element_type.py
+++ b/arkindex/documents/tests/test_element_type.py
@@ -53,12 +53,38 @@ class TestElementType(FixtureAPITestCase):
 
     def test_create(self):
         self.client.force_login(self.superuser)
-        response = self.client.post(reverse('api:element-type-create'), {
-            'corpus': self.corpus.id,
-            'slug': 'New_element_type',
-            'display_name': 'New element type',
-        }, format='json')
-        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+        with self.assertNumQueries(5):
+            response = self.client.post(reverse('api:element-type-create'), {
+                'corpus': self.corpus.id,
+                'slug': 'New_element_type',
+                'display_name': 'New element type',
+            }, format='json')
+            self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+        data = response.json()
+        self.assertIn('id', data)
+        element_type = ElementType.objects.get(id=data['id'])
+        self.assertDictEqual(
+            data,
+            {
+                'id': str(element_type.id),
+                'corpus': str(element_type.corpus.id),
+                'slug': element_type.slug,
+                'display_name': element_type.display_name,
+                'folder': element_type.folder,
+                'color': '28b62c'
+            }
+        )
+
+    def test_create_with_color(self):
+        self.client.force_login(self.superuser)
+        with self.assertNumQueries(5):
+            response = self.client.post(reverse('api:element-type-create'), {
+                'corpus': self.corpus.id,
+                'slug': 'New_element_type',
+                'display_name': 'New element type',
+                'color': 'd90dc4'
+            }, format='json')
+            self.assertEqual(response.status_code, status.HTTP_201_CREATED)
         data = response.json()
         self.assertIn('id', data)
         element_type = ElementType.objects.get(id=data['id'])
@@ -69,10 +95,23 @@ class TestElementType(FixtureAPITestCase):
                 'corpus': str(element_type.corpus.id),
                 'slug': element_type.slug,
                 'display_name': element_type.display_name,
-                'folder': element_type.folder
+                'folder': element_type.folder,
+                'color': 'd90dc4'
             }
         )
 
+    def test_create_with_color_valid_length(self):
+        self.client.force_login(self.superuser)
+        with self.assertNumQueries(3):
+            response = self.client.post(reverse('api:element-type-create'), {
+                'corpus': self.corpus.id,
+                'slug': 'New_element_type',
+                'display_name': 'New element type',
+                'color': 'chouquette'
+            }, format='json')
+            self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        self.assertDictEqual(response.json(), {'color': ['Ensure this field has no more than 6 characters.']})
+
     def test_partial_update_requires_login(self):
         response = self.client.post(reverse('api:element-type', kwargs={'pk': self.element_type.id}), {
             'slug': 'New_element_type',
@@ -108,16 +147,28 @@ class TestElementType(FixtureAPITestCase):
         self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
         self.assertDictEqual(response.json(), {'slug': ['Slug must be unique']})
 
+    def test_partial_update_color_valid_length(self):
+        self.client.force_login(self.superuser)
+        with self.assertNumQueries(3):
+            response = self.client.patch(
+                reverse('api:element-type', kwargs={'pk': self.element_type.id}),
+                {'color': 'chouquette'}, format='json'
+            )
+            self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        self.assertDictEqual(response.json(), {'color': ['Ensure this field has no more than 6 characters.']})
+
     def test_partial_update(self):
         self.client.force_login(self.superuser)
         cases = [
             {'slug': 'new_element_type'},
             {'display_name': 'New element type'},
             {'folder': True},
+            {'color': '4287f5'},
             {
                 'slug': 'new_element_type',
                 'display_name': 'New element type',
                 'folder': False,
+                'color': '4287f5'
             },
             {
                 'slug': 'new_element_type',
@@ -126,7 +177,7 @@ class TestElementType(FixtureAPITestCase):
         ]
 
         for update_data in cases:
-            with self.subTest(**update_data):
+            with self.subTest(**update_data) and self.assertNumQueries(7):
                 response = self.client.patch(
                     reverse('api:element-type', kwargs={'pk': self.element_type.id}),
                     update_data,
@@ -142,7 +193,8 @@ class TestElementType(FixtureAPITestCase):
                         'id': str(self.element_type.id),
                         'slug': self.element_type.slug,
                         'display_name': self.element_type.display_name,
-                        'folder': self.element_type.folder
+                        'folder': self.element_type.folder,
+                        'color': self.element_type.color
                     }
                 )
 
@@ -182,13 +234,30 @@ class TestElementType(FixtureAPITestCase):
         self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
         self.assertDictEqual(response.json(), {'slug': ['Slug must be unique']})
 
+    def test_update_color_valid_length(self):
+        self.client.force_login(self.superuser)
+        with self.assertNumQueries(3):
+            response = self.client.put(
+                reverse('api:element-type', kwargs={'pk': self.element_type.id}),
+                {
+                    'slug': 'New_element_type',
+                    'display_name': 'New element type',
+                    'color': 'chouquette'
+                },
+                format='json'
+            )
+            self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        self.assertDictEqual(response.json(), {'color': ['Ensure this field has no more than 6 characters.']})
+
     def test_update(self):
         self.client.force_login(self.superuser)
-        response = self.client.put(reverse('api:element-type', kwargs={'pk': self.element_type.id}), {
-            'slug': 'New_element_type',
-            'display_name': 'New element type',
-        }, format='json')
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        with self.assertNumQueries(6):
+            response = self.client.put(reverse('api:element-type', kwargs={'pk': self.element_type.id}), {
+                'slug': 'New_element_type',
+                'display_name': 'New element type',
+                'color': '4287f5'
+            }, format='json')
+            self.assertEqual(response.status_code, status.HTTP_200_OK)
         data = response.json()
         self.element_type.refresh_from_db()
         self.assertIn('id', data)
@@ -198,7 +267,8 @@ class TestElementType(FixtureAPITestCase):
                 'id': str(self.element_type.id),
                 'slug': self.element_type.slug,
                 'display_name': self.element_type.display_name,
-                'folder': self.element_type.folder
+                'folder': self.element_type.folder,
+                'color': '4287f5'
             }
         )
 
diff --git a/arkindex/process/tests/test_transkribus_import.py b/arkindex/process/tests/test_transkribus_import.py
index 1c8d37382542cbdb5b12873546db6796b34d8a77..7c2a6f75d6f06d2ee5e9ac44279d6b2ce7c62c09 100644
--- a/arkindex/process/tests/test_transkribus_import.py
+++ b/arkindex/process/tests/test_transkribus_import.py
@@ -124,6 +124,7 @@ class TestTranskribusImport(FixtureAPITestCase):
                 "slug",
                 "display_name",
                 "folder",
+                "color",
             )),
             [{
                 "folder": False,
diff --git a/arkindex/project/default_corpus.py b/arkindex/project/default_corpus.py
index a8fd62ee6fbf6074642a0a041d97d4b59d3da4b6..691fe36012ae3e05e840eba14bb634d0d1dc1009 100644
--- a/arkindex/project/default_corpus.py
+++ b/arkindex/project/default_corpus.py
@@ -9,26 +9,32 @@ DEFAULT_CORPUS_TYPES = [
         'slug': 'folder',
         'display_name': 'Folder',
         'folder': True,
+        'color': '28b62c',
     },
     {
         'slug': 'page',
         'display_name': 'Page',
+        'color': '28b62c',
     },
     {
         'slug': 'text_line',
         'display_name': 'Text line',
+        'color': '115eed',
     },
     {
         'slug': 'text_zone',
         'display_name': 'Text zone',
+        'color': '0ac4be'
     },
     {
         'slug': 'word',
         'display_name': 'Word',
+        'color': 'a0c704',
     },
     {
         'slug': 'paragraph',
         'display_name': 'Paragraph',
+        'color': '642aeb'
     }
 ]
 
@@ -37,17 +43,21 @@ DEFAULT_TRANSKRIBUS_TYPES = [
         'slug': 'volume',
         'display_name': 'Volume',
         'folder': True,
+        'color': '28b62c',
     },
     {
         'slug': 'page',
         'display_name': 'Page',
+        'color': '28b62c',
     },
     {
         'slug': 'text_line',
         'display_name': 'Text line',
+        'color': '115eed',
     },
     {
         'slug': 'paragraph',
         'display_name': 'Paragraph',
+        'color': '642aeb'
     }
 ]
diff --git a/arkindex/sql_validation/process_elements_filter_type.sql b/arkindex/sql_validation/process_elements_filter_type.sql
index 9d48f344920c7cfc67dd97120b864d49580ba7ac..4046f2f61dd132dbbddf4a5985156ab28e7c4300 100644
--- a/arkindex/sql_validation/process_elements_filter_type.sql
+++ b/arkindex/sql_validation/process_elements_filter_type.sql
@@ -68,7 +68,8 @@ SELECT "documents_elementtype"."id",
        "documents_elementtype"."slug",
        "documents_elementtype"."display_name",
        "documents_elementtype"."folder",
-       "documents_elementtype"."indexable"
+       "documents_elementtype"."indexable",
+       "documents_elementtype"."color"
 FROM "documents_elementtype"
 WHERE "documents_elementtype"."id" = '{type_id}'::uuid
 LIMIT 21;
diff --git a/arkindex/users/tests/test_registration.py b/arkindex/users/tests/test_registration.py
index a910d494389ab9ae1220172c9683b0c0fef2af14..0277e1be03aba4203d8539b5e17580744daa6100 100644
--- a/arkindex/users/tests/test_registration.py
+++ b/arkindex/users/tests/test_registration.py
@@ -150,6 +150,7 @@ class TestRegistration(FixtureAPITestCase):
                 'slug',
                 'display_name',
                 'folder',
+                'color',
             )),
             [{
                 'folder': False,