diff --git a/lib/phlexy_ui/file_input.rb b/lib/phlexy_ui/file_input.rb
new file mode 100644
index 0000000..b2d3fc7
--- /dev/null
+++ b/lib/phlexy_ui/file_input.rb
@@ -0,0 +1,124 @@
+# frozen_string_literal: true
+
+module PhlexyUI
+ # @component html class="file-input"
+ class FileInput < Base
+ def initialize(*, as: :input, **)
+ super(*, **)
+ @as = as
+ end
+
+ def view_template(&)
+ generate_classes!(
+ # "file-input"
+ component_html_class: :"file-input",
+ modifiers_map: modifiers,
+ base_modifiers:,
+ options:
+ ).then do |classes|
+ public_send(as, type: :file, class: classes, **options, &)
+ end
+ end
+
+ register_modifiers(
+ # "sm:file-input-ghost"
+ # "@sm:file-input-ghost"
+ # "md:file-input-ghost"
+ # "@md:file-input-ghost"
+ # "lg:file-input-ghost"
+ # "@lg:file-input-ghost"
+ ghost: "file-input-ghost",
+ # "sm:file-input-xs"
+ # "@sm:file-input-xs"
+ # "md:file-input-xs"
+ # "@md:file-input-xs"
+ # "lg:file-input-xs"
+ # "@lg:file-input-xs"
+ xs: "file-input-xs",
+ # "sm:file-input-sm"
+ # "@sm:file-input-sm"
+ # "md:file-input-sm"
+ # "@md:file-input-sm"
+ # "lg:file-input-sm"
+ # "@lg:file-input-sm"
+ sm: "file-input-sm",
+ # "sm:file-input-md"
+ # "@sm:file-input-md"
+ # "md:file-input-md"
+ # "@md:file-input-md"
+ # "lg:file-input-md"
+ # "@lg:file-input-md"
+ md: "file-input-md",
+ # "sm:file-input-lg"
+ # "@sm:file-input-lg"
+ # "md:file-input-lg"
+ # "@md:file-input-lg"
+ # "lg:file-input-lg"
+ # "@lg:file-input-lg"
+ lg: "file-input-lg",
+ # "sm:file-input-xl"
+ # "@sm:file-input-xl"
+ # "md:file-input-xl"
+ # "@md:file-input-xl"
+ # "lg:file-input-xl"
+ # "@lg:file-input-xl"
+ xl: "file-input-xl",
+ # "sm:file-input-neutral"
+ # "@sm:file-input-neutral"
+ # "md:file-input-neutral"
+ # "@md:file-input-neutral"
+ # "lg:file-input-neutral"
+ # "@lg:file-input-neutral"
+ neutral: "file-input-neutral",
+ # "sm:file-input-primary"
+ # "@sm:file-input-primary"
+ # "md:file-input-primary"
+ # "@md:file-input-primary"
+ # "lg:file-input-primary"
+ # "@lg:file-input-primary"
+ primary: "file-input-primary",
+ # "sm:file-input-secondary"
+ # "@sm:file-input-secondary"
+ # "md:file-input-secondary"
+ # "@md:file-input-secondary"
+ # "lg:file-input-secondary"
+ # "@lg:file-input-secondary"
+ secondary: "file-input-secondary",
+ # "sm:file-input-accent"
+ # "@sm:file-input-accent"
+ # "md:file-input-accent"
+ # "@md:file-input-accent"
+ # "lg:file-input-accent"
+ # "@lg:file-input-accent"
+ accent: "file-input-accent",
+ # "sm:file-input-info"
+ # "@sm:file-input-info"
+ # "md:file-input-info"
+ # "@md:file-input-info"
+ # "lg:file-input-info"
+ # "@lg:file-input-info"
+ info: "file-input-info",
+ # "sm:file-input-success"
+ # "@sm:file-input-success"
+ # "md:file-input-success"
+ # "@md:file-input-success"
+ # "lg:file-input-success"
+ # "@lg:file-input-success"
+ success: "file-input-success",
+ # "sm:file-input-warning"
+ # "@sm:file-input-warning"
+ # "md:file-input-warning"
+ # "@md:file-input-warning"
+ # "lg:file-input-warning"
+ # "@lg:file-input-warning"
+ warning: "file-input-warning",
+ # "sm:file-input-error"
+ # "@sm:file-input-error"
+ # "md:file-input-error"
+ # "@md:file-input-error"
+ # "lg:file-input-error"
+ # "@lg:file-input-error"
+ error: "file-input-error"
+ )
+ end
+end
diff --git a/spec/lib/phlexy_ui/file_input_spec.rb b/spec/lib/phlexy_ui/file_input_spec.rb
new file mode 100644
index 0000000..88ee92c
--- /dev/null
+++ b/spec/lib/phlexy_ui/file_input_spec.rb
@@ -0,0 +1,100 @@
+require "spec_helper"
+
+describe PhlexyUI::FileInput do
+ subject(:output) { render described_class.new }
+
+ it "is expected to match the formatted HTML" do
+ expected_html = html <<~HTML
+
+ HTML
+
+ is_expected.to eq(expected_html)
+ end
+
+ describe "conditions" do
+ {
+ ghost: "file-input-ghost",
+ xs: "file-input-xs",
+ sm: "file-input-sm",
+ md: "file-input-md",
+ lg: "file-input-lg",
+ xl: "file-input-xl",
+ neutral: "file-input-neutral",
+ primary: "file-input-primary",
+ secondary: "file-input-secondary",
+ accent: "file-input-accent",
+ info: "file-input-info",
+ success: "file-input-success",
+ warning: "file-input-warning",
+ error: "file-input-error"
+ }.each do |modifier, css|
+ context "when given :#{modifier} modifier" do
+ subject(:output) { render described_class.new(modifier) }
+
+ it "renders it apart from the main class" do
+ expected_html = html <<~HTML
+
+ HTML
+
+ expect(output).to eq(expected_html)
+ end
+ end
+ end
+
+ context "when given multiple conditions" do
+ subject(:output) { render described_class.new(:primary, :lg) }
+
+ it "renders them separately" do
+ expected_html = html <<~HTML
+
+ HTML
+
+ expect(output).to eq(expected_html)
+ end
+ end
+ end
+
+ describe "data" do
+ subject(:output) do
+ render described_class.new(data: {foo: "bar"})
+ end
+
+ it "renders it correctly" do
+ expected_html = html <<~HTML
+
+ HTML
+
+ expect(output).to eq(expected_html)
+ end
+ end
+
+ describe "responsiveness" do
+ %i[sm md lg xl @sm @md @lg @xl].each do |viewport|
+ context "when given an :#{viewport} responsive option" do
+ subject(:output) do
+ render described_class.new(:primary, responsive: {viewport => :secondary})
+ end
+
+ it "renders it separately with a responsive prefix" do
+ expected_html = html <<~HTML
+
+ HTML
+
+ expect(output).to eq(expected_html)
+ end
+ end
+ end
+ end
+
+ describe "passing :as option" do
+ subject(:output) { render described_class.new(as: :button) }
+
+ it "renders as the given tag" do
+ expected_html = html <<~HTML
+
+ HTML
+
+ expect(output).to eq(expected_html)
+ end
+ end
+end