diff --git a/functions.php b/functions.php
index 71a2876b..f903ee54 100644
--- a/functions.php
+++ b/functions.php
@@ -10,6 +10,8 @@ function () {
\BEA\Theme\Framework\Framework::get_container()->boot_services();
}
);
+
+require_once __DIR__ . '/inc/Helpers/Misc.php';
require_once __DIR__ . '/inc/Helpers/Svg.php';
require_once __DIR__ . '/inc/Helpers/Formatting/Escape.php';
require_once __DIR__ . '/inc/Helpers/Formatting/Image.php';
diff --git a/inc/Helpers/Misc.php b/inc/Helpers/Misc.php
new file mode 100644
index 00000000..3bf68d28
--- /dev/null
+++ b/inc/Helpers/Misc.php
@@ -0,0 +1,124 @@
+ '',
+ 'file_name' => '',
+ 'path' => '',
+ 'size' => '',
+ 'ext' => '',
+ 'caption' => '',
+ ];
+
+ if ( empty( $file_href ) ) {
+ return $file_infos;
+ }
+
+ $file_path = get_attached_file( $file_id );
+
+ if ( empty( $file_path ) ) {
+ return $file_infos;
+ }
+
+ $file_ext = pathinfo( $file_path, PATHINFO_EXTENSION );
+
+ if ( empty( $file_ext ) ) {
+ return $file_infos;
+ }
+
+ $file_size = (string) size_format( wp_filesize( $file_path ) );
+ $file_name = (string) ( get_the_title( $file_id ) ?? '' );
+
+ return [
+ 'file_name' => $file_name,
+ 'details' => get_file_detail( $file_name, $file_ext, $file_size ),
+ 'details_accessible' => get_file_detail( $file_name, $file_ext, get_accessible_file_size_label( $file_size ) ),
+ 'href' => $file_href,
+ 'caption' => (string) wp_get_attachment_caption( $file_id ),
+ 'icon' => get_file_icon( $file_ext ),
+ ];
+}
+
+/**
+ * Get file details
+ *
+ * @param string $file_name
+ * @param string $file_ext
+ * @param string $file_size
+ *
+ * @return string $file_detail
+ */
+function get_file_detail( string $file_name, string $file_ext, string $file_size ): string {
+ $details = [];
+
+ if ( ! empty( $file_name ) ) {
+ $details[] = $file_name;
+ }
+
+ if ( ! empty( $file_ext ) ) {
+ $details[] = strtoupper( $file_ext );
+ }
+
+ if ( ! empty( $file_size ) ) {
+ $details[] = $file_size;
+ }
+
+ return implode( ' – ', $details );
+}
+
+/**
+ * Get accessible file size label
+ *
+ * @param string $file_size
+ *
+ * @return string
+ */
+function get_accessible_file_size_label( string $file_size ): string {
+ // Extract value and unit from file size (e.g., "7ko" → "7" + "ko").
+ preg_match( '/^([\d.,]+)\s*([a-zA-Z]+)$/', $file_size, $matches );
+ $value = $matches[1] ?? '';
+ $int_value = (int) $value; // Cast to int for _n() pluralization.
+ $unit = strtolower( $matches[2] ?? '' );
+
+ /* translators: file size units (byte, kilobyte, megabyte, etc.) */
+ $unit_label = match ( $unit ) {
+ 'b', 'o' => _n( 'byte', 'bytes', $int_value, 'beapi-frontend-framework' ),
+ 'kb', 'ko' => _n( 'kilobyte', 'kilobytes', $int_value, 'beapi-frontend-framework' ),
+ 'mb', 'mo' => _n( 'megabyte', 'megabytes', $int_value, 'beapi-frontend-framework' ),
+ 'gb', 'go' => _n( 'gigabyte', 'gigabytes', $int_value, 'beapi-frontend-framework' ),
+ 'tb', 'to' => _n( 'terabyte', 'terabytes', $int_value, 'beapi-frontend-framework' ),
+ default => null,
+ };
+
+ if ( null === $unit_label ) {
+ return $file_size;
+ }
+
+ return $value . ' ' . $unit_label;
+}
+
+/**
+ * @param string $file_ext
+ *
+ * @return string
+ */
+function get_file_icon( string $file_ext ): string {
+
+ $file_icon = 'file';
+
+ if ( in_array( $file_ext, [ 'jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico' ], true ) ) {
+ $file_icon = 'file-image';
+ }
+
+ return $file_icon;
+}
diff --git a/src/img/icons/sprite/file-image.svg b/src/img/icons/sprite/file-image.svg
new file mode 100644
index 00000000..973318f4
--- /dev/null
+++ b/src/img/icons/sprite/file-image.svg
@@ -0,0 +1 @@
+
diff --git a/src/img/icons/sprite/file.svg b/src/img/icons/sprite/file.svg
new file mode 100644
index 00000000..b9469573
--- /dev/null
+++ b/src/img/icons/sprite/file.svg
@@ -0,0 +1 @@
+