diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
index 2c6ea3d..b268ef3 100644
--- a/.idea/deploymentTargetSelector.xml
+++ b/.idea/deploymentTargetSelector.xml
@@ -5,9 +5,6 @@
-
-
-
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index a425311..b29afa8 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -14,7 +14,6 @@
-
diff --git a/app/src/main/kotlin/com/yourssu/handy/demo/OutlinedTextFieldPreview.kt b/app/src/main/kotlin/com/yourssu/handy/demo/OutlinedTextFieldPreview.kt
new file mode 100644
index 0000000..2ea8038
--- /dev/null
+++ b/app/src/main/kotlin/com/yourssu/handy/demo/OutlinedTextFieldPreview.kt
@@ -0,0 +1,60 @@
+package com.yourssu.handy.demo
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.yourssu.handy.compose.HandyTheme
+import com.yourssu.handy.compose.icons.HandyIcons
+import com.yourssu.handy.compose.icons.filled.Cancel
+import com.yourssu.handy.compose.textfield.OutlinedTextField
+
+@Preview
+@Composable
+fun OutlinedTextFieldPreview() {
+ HandyTheme {
+ Column(
+ modifier = Modifier
+ .wrapContentSize()
+ .padding(20.dp),
+ verticalArrangement = Arrangement.spacedBy(20.dp)
+ ) {
+ val (text, onValueChange) = remember { mutableStateOf("") }
+ OutlinedTextField(
+ value = text,
+ placeholder = "placeholder",
+ trailingIcon = HandyIcons.Filled.Cancel,
+ onValueChange = onValueChange,
+ )
+
+ OutlinedTextField(
+ value = text,
+ placeholder = "placeholder",
+ trailingIcon = HandyIcons.Filled.Cancel,
+ isError = true,
+ onValueChange = onValueChange,
+ )
+
+ OutlinedTextField(
+ value = text,
+ placeholder = "placeholder",
+ trailingIcon = HandyIcons.Filled.Cancel,
+ enabled = false,
+ onValueChange = onValueChange,
+ )
+
+ OutlinedTextField(
+ value = text,
+ placeholder = "placeholder",
+ enabled = false,
+ onValueChange = onValueChange,
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/yourssu/handy/demo/TextFieldPreview.kt b/app/src/main/kotlin/com/yourssu/handy/demo/TextFieldPreview.kt
new file mode 100644
index 0000000..c78ef2f
--- /dev/null
+++ b/app/src/main/kotlin/com/yourssu/handy/demo/TextFieldPreview.kt
@@ -0,0 +1,57 @@
+package com.yourssu.handy.demo
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.yourssu.handy.compose.HandyTheme
+import com.yourssu.handy.compose.icons.HandyIcons
+import com.yourssu.handy.compose.icons.filled.Cancel
+import com.yourssu.handy.compose.textfield.TextField
+
+@Preview
+@Composable
+fun TextFieldPreview() {
+ HandyTheme {
+ Column(
+ modifier = Modifier.wrapContentSize().padding(20.dp),
+ verticalArrangement = Arrangement.spacedBy(20.dp)
+ ) {
+ val (text, onValueChange) = remember { mutableStateOf("") }
+ TextField(
+ value = text,
+ textLabel = "Label",
+ helperText = "Helper Text",
+ placeholder = "placeholder",
+ trailingIcon = HandyIcons.Filled.Cancel,
+ onValueChange = onValueChange
+ )
+
+ TextField(
+ value = text,
+ textLabel = "Label",
+ helperText = "Helper Text",
+ placeholder = "placeholder",
+ trailingIcon = HandyIcons.Filled.Cancel,
+ isError = true,
+ onValueChange = onValueChange
+ )
+
+ TextField(
+ value = text,
+ textLabel = "Label",
+ helperText = "Helper Text",
+ placeholder = "placeholder",
+ trailingIcon = HandyIcons.Filled.Cancel,
+ enabled = false,
+ onValueChange = onValueChange
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/compose/src/main/kotlin/com/yourssu/handy/compose/textfield/OutlinedTextField.kt b/compose/src/main/kotlin/com/yourssu/handy/compose/textfield/OutlinedTextField.kt
new file mode 100644
index 0000000..eac5229
--- /dev/null
+++ b/compose/src/main/kotlin/com/yourssu/handy/compose/textfield/OutlinedTextField.kt
@@ -0,0 +1,130 @@
+package com.yourssu.handy.compose.textfield
+
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsFocusedAsState
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.unit.dp
+import com.yourssu.handy.compose.HandyTheme
+import com.yourssu.handy.compose.Icon
+import com.yourssu.handy.compose.Surface
+import com.yourssu.handy.compose.Text
+import com.yourssu.handy.compose.foundation.HandyTypography
+import com.yourssu.handy.compose.foundation.Radius
+
+@Composable
+fun OutlinedTextField(
+ value: String,
+ onValueChange: (String) -> Unit,
+ modifier: Modifier = Modifier,
+ placeholder: String? = null,
+ trailingIcon: ImageVector? = null,
+ enabled: Boolean = true,
+ isError: Boolean = false,
+ isSingleLine: Boolean = true,
+ onClickTrailingIcon: () -> Unit = {}
+) {
+ val interactionSource = remember { MutableInteractionSource() }
+ val isFocused = interactionSource.collectIsFocusedAsState().value
+
+ val (borderColor, cursorColor, textColor, placeholderTextColor) = getTextFieldStyle(
+ enabled,
+ isError,
+ isFocused
+ )
+
+ Surface(
+ rounding = Radius.M.dp,
+ backgroundColor = HandyTheme.colors.bgBasicLight,
+ border = BorderStroke(1.dp, borderColor),
+ modifier = modifier.fillMaxWidth()
+ ) {
+ Row(
+ modifier = Modifier.padding(start = 16.dp, end = 12.dp, top = 12.dp, bottom = 12.dp),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ ) {
+ BasicTextField(
+ value = value,
+ onValueChange = onValueChange,
+ modifier = Modifier.fillMaxWidth().weight(1f),
+ textStyle = HandyTypography.B1Rg16.toTextStyle().copy(color = textColor),
+ enabled = enabled,
+ singleLine = isSingleLine,
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text),
+ interactionSource = interactionSource,
+ cursorBrush = SolidColor(cursorColor),
+ decorationBox = { innerTextField ->
+ if (value.isEmpty() && placeholder != null) {
+ Text(
+ text = placeholder,
+ style = HandyTypography.B1Rg16,
+ color = placeholderTextColor
+ )
+ }
+ innerTextField()
+ }
+ )
+
+ Spacer(modifier = Modifier.width(12.dp))
+
+ trailingIcon?.let {
+ Icon(
+ imageVector = it,
+ contentDescription = null,
+ tint = HandyTheme.colors.iconBasicTertiary,
+ modifier = Modifier
+ .clickable(
+ indication = null,
+ interactionSource = interactionSource,
+ onClick = onClickTrailingIcon
+ )
+ )
+ }
+ }
+ }
+}
+
+data class TextFieldStyle(
+ val borderColor: Color,
+ val cursorColor: Color,
+ val textColor: Color,
+ val placeholderTextColor: Color
+)
+
+@Composable
+private fun getTextFieldStyle(
+ enabled: Boolean,
+ isError: Boolean,
+ isFocused: Boolean
+): TextFieldStyle {
+ return TextFieldStyle(
+ borderColor = when {
+ !enabled -> HandyTheme.colors.bgBasicLight
+ isError -> HandyTheme.colors.lineStatusNegative
+ isFocused -> HandyTheme.colors.lineStatusPositive
+ else -> HandyTheme.colors.bgBasicLight
+ },
+ cursorColor = when {
+ isError && isFocused -> HandyTheme.colors.lineStatusNegative
+ isFocused -> HandyTheme.colors.lineStatusPositive
+ else -> HandyTheme.colors.textBasicPrimary
+ },
+ textColor = if (!enabled) HandyTheme.colors.textBasicDisabled else HandyTheme.colors.textBasicPrimary,
+ placeholderTextColor = if (!enabled) HandyTheme.colors.textBasicDisabled else HandyTheme.colors.textBasicTertiary
+ )
+}
\ No newline at end of file
diff --git a/compose/src/main/kotlin/com/yourssu/handy/compose/textfield/TextArea.kt b/compose/src/main/kotlin/com/yourssu/handy/compose/textfield/TextArea.kt
new file mode 100644
index 0000000..cc9e251
--- /dev/null
+++ b/compose/src/main/kotlin/com/yourssu/handy/compose/textfield/TextArea.kt
@@ -0,0 +1,82 @@
+package com.yourssu.handy.compose.textfield
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.yourssu.handy.compose.HandyTheme
+import com.yourssu.handy.compose.Text
+import com.yourssu.handy.compose.foundation.HandyTypography
+
+@Composable
+fun TextArea(
+ value: String,
+ onValueChange: (String) -> Unit,
+ modifier: Modifier = Modifier,
+ placeholder: String? = null,
+ helperText: String? = null,
+ maxCharacters: Int = Int.MAX_VALUE,
+ enabled: Boolean = true,
+ isError: Boolean = false,
+) {
+ val textHelperColor = when {
+ isError -> HandyTheme.colors.lineStatusNegative
+ else -> HandyTheme.colors.textBasicTertiary
+ }
+
+ Column(
+ modifier = modifier
+ ) {
+ OutlinedTextField(
+ value = value,
+ onValueChange = {
+ onValueChange(it)
+ },
+ placeholder = placeholder,
+ enabled = enabled,
+ isError = isError,
+ isSingleLine = false,
+ modifier = Modifier
+ )
+
+ helperText?.let {
+ Text(
+ text = it,
+ style = HandyTypography.B5Rg12,
+ color = textHelperColor,
+ modifier = Modifier.padding(top = 4.dp)
+ )
+ }
+ }
+}
+
+@Preview
+@Composable
+fun TextAreaPreview() {
+ HandyTheme {
+ Column(
+ modifier = Modifier.wrapContentSize().padding(20.dp),
+ verticalArrangement = Arrangement.spacedBy(20.dp)
+ ) {
+ val (text, onValueChange) = remember {
+ androidx.compose.runtime.mutableStateOf(
+ "inputText inputText asdaasdas inputText inputText asdaasdas inputText inputText asdaasdas"
+ )
+ }
+
+ TextArea(
+ value = text,
+ onValueChange = onValueChange,
+ placeholder = "Placeholder",
+ helperText = "Helper Text",
+ maxCharacters = 20,
+ enabled = true,
+ )
+ }
+ }
+}
diff --git a/compose/src/main/kotlin/com/yourssu/handy/compose/textfield/TextField.kt b/compose/src/main/kotlin/com/yourssu/handy/compose/textfield/TextField.kt
new file mode 100644
index 0000000..d3afa5b
--- /dev/null
+++ b/compose/src/main/kotlin/com/yourssu/handy/compose/textfield/TextField.kt
@@ -0,0 +1,68 @@
+package com.yourssu.handy.compose.textfield
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.unit.dp
+import com.yourssu.handy.compose.HandyTheme
+import com.yourssu.handy.compose.Text
+import com.yourssu.handy.compose.foundation.HandyTypography
+
+@Composable
+fun TextField(
+ value: String,
+ onValueChange: (String) -> Unit,
+ modifier: Modifier = Modifier,
+ textLabel: String? = null,
+ helperText: String? = null,
+ placeholder: String? = null,
+ trailingIcon: ImageVector? = null,
+ enabled: Boolean = true,
+ isError: Boolean = false,
+ isSingleLine: Boolean = true,
+ onClickTrailingIcon: () -> Unit = {}
+) {
+
+ val helperTextColor = when {
+ isError -> HandyTheme.colors.lineStatusNegative
+ else -> HandyTheme.colors.textBasicTertiary
+ }
+
+ Column(
+ modifier = modifier
+ ) {
+ textLabel?.let {
+ Text(
+ text = it,
+ style = HandyTypography.B5Rg12,
+ color = HandyTheme.colors.textBasicTertiary,
+ )
+ }
+
+ Spacer(modifier = Modifier.height(4.dp))
+
+ OutlinedTextField(
+ value = value,
+ onValueChange = onValueChange,
+ placeholder = placeholder,
+ trailingIcon = trailingIcon,
+ enabled = enabled,
+ isError = isError,
+ isSingleLine = isSingleLine,
+ onClickTrailingIcon = onClickTrailingIcon
+ )
+
+ Spacer(modifier = Modifier.height(4.dp))
+
+ helperText?.let {
+ Text(
+ text = it,
+ style = HandyTypography.B5Rg12,
+ color = helperTextColor,
+ )
+ }
+ }
+}
\ No newline at end of file